I have the following simple webservice declared as @Stateless
EJB running on GlassFish 3.1.2.2 with EclipseLink 2.4.1 using a JTA DataSource
to connect to a MySQL database:
@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 class of 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);
}
}
}
}
@EmbeddableId
TimeRowPK
:
@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
(without the <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 declaration:
@Path("row")
@Stateless
public class TimeRowWebService {
@PersistenceContext(unitName = "test")
private EntityManager em;
...
}
The problem is that if the entity exists, the changes get stored only in the PersistenceContext
, but they are not committed to the database. Meaning that I can retrieve the correct data with the changes, but for example if I restart the AS, the changes are gone. There are no errors in the AS log.
So I guess I have to do some bean level manual transaction handling to get this work. What exactly do I have to add to get this working?
After struggling to get this working the @EmbeddedId
way, I've tried to implement it by using a generated @Id
, and adding a unique constraint for the two key fields afterwards.
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;
...
}
Additionally, I've changed the DB engine from MyISAM
to InnoDB
to have better foreign key support. To do this, I've added default-storage-engine = innodb
to /etc/mysql/my.cnf
under the [mysqld]
section.
After generating the DB structure with DDL, I've added a unique constraint for USERID
and DAY
:
alter table TIMEROW add unique index(USERID,DAY);
Now it works and data is modified correctly :) A huge thank you to everybody who contributed to this question!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With