2013-04-12 12 views
5

mam następujący prosty usługa zadeklarowane jako @Stateless EJB działa na GlassFish 3.1.2.2 z EclipseLink 2.4.1 używając JTA DataSource aby połączyć się z bazą danych MySQL:@Stateless webservice with JPA + JTA: Jak zatwierdzać zmiany zarządzanego obiektu?

@POST 
@Consumes(MediaType.APPLICATION_JSON) 
public Response update(TimeRow e) throws Exception { 
    if ((e.getKey() == null) || !e.getKey().keyValid()) { 
     return Response.status(400).build(); 
    } 

    TimeRow existing = em.find(TimeRow.class, e.getKey()); 
    if (existing == null) { 
     em.persist(e); 
    } else { 
     existing.setValues(e.getValues()); 
     em.flush(); 
    } 
    return Response.status(204).build(); 
} 

klasy podmiotu TimeRow:

@Entity 
@NamedQueries({ 
    @NamedQuery(name="TimeRow.findAllByUser", 
     query="SELECT t FROM TimeRow t WHERE t.table.userId = :uid") 
}) 
public class TimeRow implements Serializable { 

    @EmbeddedId 
    private TimeRowPK key; 

    @MapsId("userId") 
    @JoinColumn(name = "USERID", referencedColumnName = "userId") 
    @ManyToOne 
    private UserTable table; 
    @Column(name="ROWVALUES") 
    private List<Double> values; 

    public TimeRow() { 
     this.key = new TimeRowPK(); 
     this.values = new ArrayList<Double>(20); 
     extendValuesTo20(); 
    } 

    public TimeRow(String uid, Date date) { 
     this.key = new TimeRowPK(date, uid); 
     this.table = new UserTable(uid); 
     this.values = new ArrayList<Double>(20); 
     extendValuesTo20(); 
    } 

    public List<Double> getValues() { 
     return values; 
    } 

    public void setValues(List<Double> values) { 
     this.values = values; 
     extendValuesTo20(); 
    } 

    private void extendValuesTo20() { 
     if (this.values.size() < 20) { 
      for (int i = this.values.size(); i < 20; i++) { 
        this.values.add(0.0); 
      } 
     } 
    } 

} 

@EmbeddableIdTimeRowPK:

@Embeddable 
public class TimeRowPK implements Serializable { 

    public TimeRowPK() { } 

    public TimeRowPK(Date date, String userId) { 
     this.date = date; 
     this.userId = userId; 
    } 

    @Column(name="DAY") 
    private Date date; 
    @Column(name = "USERID") 
    private String userId; 

    public boolean keyValid() { 
     return ((date != null) && ((userId != null) && !userId.isEmpty())); 
    } 

} 

persistence.xml (bez metki <persistence>):

<persistence-unit name="test" transaction-type="JTA"> 
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> 
    <jta-data-source>jdbc/test</jta-data-source> 
    <class>com.test.TimeRow</class> 
    <class>com.test.TimeRowPK</class> 
    <class>...</class> 
    <properties> 
     <property name="eclipselink.ddl-generation" value="create-tables" /> 
     <property name="eclipselink.ddl-generation.output-mode" value="database" /> 
    </properties> 
</persistence-unit> 

deklaracja Webservice:

@Path("row") 
@Stateless 
public class TimeRowWebService { 

    @PersistenceContext(unitName = "test") 
    private EntityManager em; 

    ... 

} 

Problemem jest to, że jeśli jednostka nie istnieje, zmiany są zapisywane tylko w PersistenceContext, ale nie są zobowiązani do baza danych. Oznacza to, że mogę pobrać poprawne dane ze zmianami, ale na przykład, jeśli ponownie uruchomię AS, zmiany znikną. W dzienniku systemu AS nie ma błędów.

Sądzę więc, że muszę wykonać ręczną obsługę transakcji na poziomie fasoli, aby uzyskać tę pracę. Co dokładnie muszę dodać, aby to zadziałało?

+0

Czy ziarno zostało uznane za @Stateless? –

+0

Tak, to prawda! –

+1

W tym przypadku nie widzę tutaj błędu ... '@ Stateless' implikuje' @TransactionAttribute (REQUIRED) 'jeśli go nie zmienisz ... Może znajduje się w' MyEntity.setValues ​​() 'lub w samej deklaracji "MyEntity".Czy możesz opublikować kod 'MyEntity'? –

Odpowiedz

2

Po stara się uzyskać to działa drogę @EmbeddedId, starałem się wdrożyć go za pomocą wygenerowanego @Id i dodając unikalny ograniczenie do dwóch kluczowych obszarach później.

Podmiot

public class TimeRow implements Serializable { 

    @Id 
    @GeneratedValue 
    private long id; 

    @JoinColumn(name = "USERID", referencedColumnName = "USERID") 
    @ManyToOne(optional = false) 
    private UserTable table; 

    @Basic(optional = false) 
    @Column(name = "DAY") 
    @Temporal(TemporalType.DATE) 
    private Date date; 

    @Lob 
    @Column(name="ROWVALUES") 
    private List<Double> values; 

    ... 
} 

Dodatkowo Zmieniłem silnik DB od MyISAM do InnoDB mieć lepsze wsparcie klucza obcego. Aby to zrobić, dodałem default-storage-engine = innodb do /etc/mysql/my.cnf w sekcji [mysqld].

Po wygenerowaniu struktury DB z DDL, mam added a unique constraint dla USERID i DAY:

alter table TIMEROW add unique index(USERID,DAY); 

Teraz to działa i dane są modyfikowane poprawnie :) Ogromne podziękowania wszystkim, którzy przyczynili się do tego pytania!

1

Może haczyk jest tutaj:

public void setValues(List<Double> values) { 
    this.values = values; 
    extendValuesTo20(); 
} 

Spróbuj zmienić go używać listy kontenera na przykład:

public void setValues(List<Double> values) { 
    this.values.clear(); 
    this.values.addAll(values); 
    extendValuesTo20(); 
} 

Ponadto, jest to bardzo dziwne użycie JPA. Nigdy nie widziałem list utrzymywały się w ten sposób bez @ElementCollection

Powiązane problemy