2013-04-12 4 views
5

Ich habe die folgende einfache Webservice deklariert als @Stateless EJB auf Glassfish 3.1.2.2 mit Eclipselink läuft 2.4.1 mit einem JTA DataSource zu einer MySQL-Datenbank verbinden:@Stateless Webservice mit JPA + JTA: Wie Änderungen der verwalteten Einheit festgeschrieben werden?

@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(); 
} 

Entity Klasse von 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 (ohne <persistence> Tag):

<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> 

Webservice Erklärung:

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

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

    ... 

} 

Das Problem ist, dass, wenn die Einheit existiert, gehen die Änderungen gespeichert nur im PersistenceContext, aber sie sind nicht verpflichtet, die Datenbank. Das bedeutet, dass ich die richtigen Daten mit den Änderungen abrufen kann, aber wenn ich zum Beispiel das AS neu starte, sind die Änderungen weg. Im AS-Protokoll sind keine Fehler aufgetreten.

Also ich denke, ich muss einige manuelle Transaktion Handhabung auf Bohnenebene tun, um diese Arbeit zu bekommen. Was genau muss ich hinzufügen, damit das funktioniert?

+0

Ist die Bean @Stateless deklariert? –

+0

Ja, das stimmt! –

+1

In diesem Fall kann ich hier keinen Fehler sehen ... "@ Stateless" impliziert '@TransactionAttribute (REQUIRED)', wenn Sie es nicht ändern ... Vielleicht liegt es in 'MyEntity.setValues ​​()' oder in der 'MyEntity'-Deklaration selbst.Können Sie den 'MyEntity' Code posten? –

Antwort

2

Nach dem Kampf dieser Arbeit des @EmbeddedId Weges zu bekommen, ich habe versucht, es zu implementieren durch eine generierte @Id verwenden, und das Hinzufügen einer eindeutige Einschränkung für die beiden Schlüsselfelder danach.

Entity

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; 

    ... 
} 

Zusätzlich habe ich die DB-Engine von MyISAM zu InnoDB geändert bessere Fremdschlüssel Unterstützung zu haben. Um dies zu tun, habe ich default-storage-engine = innodb zu /etc/mysql/my.cnf unter dem [mysqld] Abschnitt hinzugefügt.

Nachdem die DB-Struktur mit DDL zu erzeugen, habe ich added a unique constraint für USERID und DAY:

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

Jetzt ist es funktioniert und Daten geändert richtig :) Ein großer Dank an alle danken, die zu dieser Frage beigetragen haben!

1

Vielleicht ist der Haken hier:

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

versuchen, es zu ändern, die Behälterliste wie in verwenden:

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

Außerdem ist es eine sehr seltsame Verwendung von PPV. Ich habe noch nie so gesehen Listen ohne @ElementCollection beharrten