How to remove an entity without loading its contents? - java

I have a service with a removeById operation whose goal is to remove an entity from the repository. As I don't want to fetch the contents of the entity before it gets removed, I use getReference() instead of find() to obtain the entity.
An entity named 'MyObject' has a #ManyToOne association with an entity named 'MySource', and the fetch type is set to LAZY.
When I get a reference to an instance of 'MyObject' and I remove that instance from the DB, both 'MyObject' and 'MySource' get hydrated first before the instance is removed.
Sample code:
#Entity
public class MyObject {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
private final MySource source;
public MyObject(MySource source) {
this.source = source;
}
protected MyObject() {
this.source = null;
}
public long id() {
return this.id;
}
public MySource source() {
return this.source;
}
#Override
public boolean equals(Object obj) {
boolean equals = this == obj;
if (!equals && obj instanceof MyObject) {
MyObject other = (MyObject) obj;
equals = other.id() == id;
}
return equals;
}
#Override
public int hashCode() {
return (int) id % 16;
}
}
#Entity
public class MySource {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
public MySource(String name) {
this.name = name;
}
protected MySource() {
this.name = "";
}
#Column
private final String name;
public long id() {
return id;
}
public String name() {
return this.name;
}
#Override
public boolean equals(Object obj) {
boolean equals = this == obj;
if (!equals && obj instanceof MySource) {
MySource other = (MySource) obj;
equals = other.id() == id;
}
return equals;
}
#Override
public int hashCode() {
return (int) id % 16;
}
}
public void removeById(long id) {
em.getTransaction().begin();
MyObject myObjectRef = em.getReference(MyObject.class, id);
em.remove(myObjectRef);
em.getTransaction().commit();
em.clear();
}
When the transaction is committed, I can see the following queries:
SELECT ID, SOURCE_ID FROM MYOBJECT WHERE (ID = ?)
SELECT ID, NAME FROM MYSOURCE WHERE (ID = ?)
DELETE FROM MYOBJECT WHERE (ID = ?)
And more surprisingly, if I change the fetch type from LAZY to EAGER in the 'MyObject' -> 'MySource' association, then I obtain:
DELETE FROM MYOBJECT WHERE (ID = ?)
This time the proxies are not hydrated.
What's wrong with LAZY loading when a proxy is used to remove an entity? And why the proxies are not hydrated when the fetch type is set to EAGER?
Did I miss something, or am I facing some bug in EclipseLink?
(EclipseLink 2.6.4 + static weaving on Java 8 SE).
Additional note
I think that the proxies are hydrated too eagerly. For instance, a statement like this.someAssociatedEntity = other.someAssociatedEntity issues a SELECT to fetch that associated entity, despite the fact that the association is annotated with a LAZY fetch type.
I would expect that a SELECT occurs only when the fields of the associated entity are accessed.
For debug purpose:
Stack trace when the MySource table is deleted just before the 'MyObject' entity is removed:
Error Code: 0
Call: SELECT ID, NAME FROM MYSOURCE WHERE (ID = ?)
bind => [1]
Query: ReadObjectQuery(name="source" referenceClass=MySource )
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:340)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.processExceptionForCommError(DatabaseAccessor.java:1620)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:676)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:560)
at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2056)
at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:306)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.selectOneRow(DatasourceCallQueryMechanism.java:714)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectOneRowFromTable(ExpressionQueryMechanism.java:2803)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectOneRow(ExpressionQueryMechanism.java:2756)
at org.eclipse.persistence.queries.ReadObjectQuery.executeObjectLevelReadQuery(ReadObjectQuery.java:555)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1175)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:904)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1134)
at org.eclipse.persistence.queries.ReadObjectQuery.execute(ReadObjectQuery.java:441)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1222)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1857)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1839)
at org.eclipse.persistence.internal.indirection.QueryBasedValueHolder.instantiate(QueryBasedValueHolder.java:133)
at org.eclipse.persistence.internal.indirection.QueryBasedValueHolder.instantiateForUnitOfWorkValueHolder(QueryBasedValueHolder.java:151)
at org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder.instantiateImpl(UnitOfWorkValueHolder.java:160)
at org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder.instantiate(UnitOfWorkValueHolder.java:234)
at org.eclipse.persistence.internal.indirection.DatabaseValueHolder.getValue(DatabaseValueHolder.java:89)
at example.MyObject._persistence_get_source(MyObject.java)
at example.MyObject._persistence_get_source_vh(MyObject.java)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.eclipse.persistence.internal.descriptors.MethodAttributeAccessor.getAttributeValueFromObject(MethodAttributeAccessor.java:82)
at org.eclipse.persistence.internal.descriptors.MethodAttributeAccessor.getAttributeValueFromObject(MethodAttributeAccessor.java:61)
at org.eclipse.persistence.mappings.DatabaseMapping.getAttributeValueFromObject(DatabaseMapping.java:657)
at org.eclipse.persistence.mappings.ForeignReferenceMapping.getAttributeValueFromObject(ForeignReferenceMapping.java:1003)
at org.eclipse.persistence.mappings.ObjectReferenceMapping.earlyPreDelete(ObjectReferenceMapping.java:841)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1412)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1531)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:278)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1169)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:134)
at test.Main.test(Main.java:203)
at test.Main.main(Main.java:42)

Related

JPA 2 merge is inserting instead of updating when primary key is UUID

For one of my entities I want to use UUID as primary key instead of a Long. The entity extends AbstractEntityUUID:
#MappedSuperclass
public abstract class AbstractEntityUUID implements Serializable {
private static final long serialVersionUID = 40076842287035126L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
public UUID getId() {
return id;
}
#Override
public int hashCode() {
if (getId() != null) {
return getId().hashCode();
}
return super.hashCode();
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
AbstractEntityUUID other = (AbstractEntityUUID) obj;
if (getId() == null || other.getId() == null) {
return false;
}
if (!getId().equals(other.getId())) {
return false;
}
return true;
}
}
The entity itself is:
#Entity
#Table(schema = "schemaName", name = "order")
#DynamicUpdate
public class Order extends AbstractEntityUUID {
private static final long serialVersionUID = 6358231835578038565L;
#Column(precision = 10, scale = 2)
protected BigDecimal itemPrice;
#Temporal(TIMESTAMP)
#Basic(fetch = LAZY)
protected Date purchaseDate;
// other properties and getters & setters ...
}
The persist method works fine:
Order order = new Order();
order.setItemPrice(BigDecimal.ONE);
order = getEM().persist(order);
A new row is created on the database table with the correct info. When there is a merge later on is when the issue occurs:
order.setPurchaseDate(new Date());
order = getEM().merge(order);
The previous row seems to be left unchanged and a new one is created with a new primary key which includes the price and date.
When the primary key is Long with GenerationType.IDENTITY the row is updated correctly instead and it works as expected.
This is run on a WildFly 16 server on an ejb 3 bean. The Hibernate implementation seems to be 5.3.9. The DB is MySQL server 5.7.
It is fine if Java generates the UUID values and I would rather not change that unless it is required to make the entity work with a UUID as primary key.
Are you sure that persist is working as expected when MySql generates the UUID? Is the id of the entity the same as the id inserted into the database?

Hibernate call DELETE from table after method end

I have problem, and I don't know how to solve it.
I have entity:
#Entity
#Table(name = "entity_languagetree")
#AttributeOverride(name = "id", column = #Column(name = "languagetree_id"))
public class LanguageTree extends BaseObject {
#ElementCollection(targetClass = java.lang.String.class, fetch = FetchType.EAGER)
#CollectionTable(name = "view_languagetree_to_stringlist")
private List<String> relationship = new ArrayList<>();
public LanguageTree() {
//
}
public List<String> getRelationship() {
return relationship;
}
public void setRelationship(List<String> relationship) {
this.relationship = relationship;
}
}
where BaseObject is
#MappedSuperclass
public class BaseObject {
#Id
#GeneratedValue
#Column(name = "entity_id")
private Long id;
/**
*
* #return true if the entity hasn't been persisted yet
*/
#Transient
public boolean isNew() {
return id == null;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Bean getBean() {
return null;
}
}
Work with object - in my servlet, I am calling jsVarTree() like this:
String var = jsVarTree();
My problem is, that after method jsVarTree is finished, hibernate delete my relationship list from entity LanguageTree. I don't know why! I am not calling any delete and etc.. (I AM SURE, I SPENT A LOT OF TIME IN DEBUGER!)
:
#Override
public String jsVarTree() {
TreeBuilder tb = new TreeBuilder(getLanguageList());
return tb.getJsVarString(); // THIS METHOD IS ONLY GETTER !!!!
}
#Override
public List<String> getLanguageList() {
LanguageTree lt = getLanguageTreeObject();
return lt.getRelationship();
}
#Override
public LanguageTree getLanguageTreeObject() {
long fakingId = languageTreeDao.getLastId();
ServerLogger.logDebug("LAST FAKING ID: " +fakingId);
return languageTreeDao.findOne(fakingId);
}
I found this log in loggor:
HibernateLog --> 15:01:03 DEBUG org.hibernate.SQL - delete from
view_languagetree_to_stringlist where LanguageTree_languagetree_id=?
Can somebody tell me, why hibernate call delete over my table?
I saw a table in phpmyadmin..
TABLE IS FULL.
String var = jsVarTree();
TABLE IS EMPTY.
Table is deleted after return tb.getJsVarString(); is finished.
Thank you for any help!

how to map Many to Many with composite key

I have the following tables
Trainingplan
TrainingplanID int(11) AI PK
Trainer int(11)
Client int(11)
validFrom date
validTo date
type int(11)
TrainingplanExercises
trainingplan int(11) PK
exercise int(11) PK
parameter int(11) PK
value varchar(45)
No I have problems connecting them with Hibernate. I did the following:
package beans;
#Entity
#Table(name = "Trainingplan")
public class Training {
private IntegerProperty id;
private ObjectProperty<Person> client;
private ObjectProperty<Person> trainer;
private ObjectProperty<Date> validFrom;
private ObjectProperty<Date> validTo;
private ObjectProperty<TrainingplanType> type;
private List<TrainingplanExercise> exercises;
public Training(int id, Person client, Person trainer, Date validFrom, Date validTo, TrainingplanType type) {
this.id = new SimpleIntegerProperty(id);
this.client = new SimpleObjectProperty<>(client);
this.trainer = new SimpleObjectProperty<>(trainer);
this.validFrom = new SimpleObjectProperty<>(validFrom);
this.validTo = new SimpleObjectProperty<>(validTo);
this.type = new SimpleObjectProperty<>(type);
exercises = FXCollections.observableArrayList();
}
public Training(Person client, Person trainer, Date validFrom, Date validTo, TrainingplanType type){
this(0, client, trainer, validFrom, validTo, type);
}
public Training(){
this(0, null,null,null,null, null);
}
#OneToOne
#JoinColumn(name = "client")
public Person getClient() {
return client.get();
}
public ObjectProperty<Person> clientProperty() {
return client;
}
public void setClient(Person client) {
this.client.set(client);
}
#OneToOne
#JoinColumn(name = "trainer")
public Person getTrainer() {
return trainer.get();
}
public ObjectProperty<Person> trainerProperty() {
return trainer;
}
public void setTrainer(Person trainer) {
this.trainer.set(trainer);
}
#Column
public Date getValidFrom() {
return validFrom.get();
}
public ObjectProperty<Date> validFromProperty() {
return validFrom;
}
public void setValidFrom(Date validFrom) {
this.validFrom.set(validFrom);
}
#Column
public Date getValidTo() {
return validTo.get();
}
public ObjectProperty<Date> validTillProperty() {
return validTo;
}
public void setValidTo(Date validTill) {
this.validTo.set(validTill);
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "TrainingplanID")
public int getId() {
return id.get();
}
public IntegerProperty idProperty() {
return id;
}
public void setId(int id) {
this.id.set(id);
}
#OneToOne
#JoinColumn(name = "type")
public TrainingplanType getType() {
return type.get();
}
public ObjectProperty<TrainingplanType> typeProperty() {
return type;
}
public void setType(TrainingplanType type) {
this.type.set(type);
}
#ManyToMany()
#JoinTable(name="TrainingplanExercises",
joinColumns={#JoinColumn(name="trainingplan")},
inverseJoinColumns={#JoinColumn(name="trainingplan"), #JoinColumn(name="exercise"), #JoinColumn(name="parameter")})
public List<TrainingplanExercise> getExercises() {
return exercises;
}
public void setExercises(List<TrainingplanExercise> exercises) {
this.exercises = exercises;
}
#Override
public String toString() {
return "Training{" +
"id=" + getId() +
", client=" + getClient() +
", trainer=" + getTrainer() +
", validFrom=" + getValidFrom() +
", validTill=" + getValidTo() +
", type=" + getType() +
'}';
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Training training = (Training) o;
return id != null ? id.equals(training.id) : training.id == null;
}
#Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
}
TrainingplanExercise.java
#Entity
#Table(name = "TrainingplanExercises")
#IdClass(TrainingplanExerciseId.class)
public class TrainingplanExercise {
private ObjectProperty<Exercise> exercise;
private ObjectProperty<Training> training;
private ObjectProperty<String> value;
private ObjectProperty<Parameter> parameter;
public TrainingplanExercise(Exercise exercise, Training training, String value, Parameter parameter){
this.exercise = new SimpleObjectProperty<>(exercise);
this.training = new SimpleObjectProperty<>(training);
this.value = new SimpleObjectProperty<>(value);
this.parameter = new SimpleObjectProperty<>(parameter);
}
public TrainingplanExercise(){
this(null,null,null,null);
}
#Id
#OneToOne
#JoinColumn(name = "parameter")
public Parameter getParameter() {
return parameter.get();
}
public ObjectProperty<Parameter> parameterProperty() {
return parameter;
}
public void setParameter(Parameter parameter) {
this.parameter.set(parameter);
}
#Id
#OneToOne
#JoinColumn(name = "exercise")
public Exercise getExercise() {
return exercise.get();
}
public ObjectProperty<Exercise> exerciseProperty() {
return exercise;
}
public void setExercise(Exercise exercise) {
this.exercise.set(exercise);
}
#Id
#OneToOne
#JoinColumn(name = "trainingplan")
public Training getTraining() {
return training.get();
}
public ObjectProperty<Training> trainingProperty() {
return training;
}
public void setTraining(Training training) {
this.training.set(training);
}
#Column(name = "value")
public String getValue(){
return value.get();
}
public ObjectProperty<String> valueProperty() {
return value;
}
public void setValue(String value) {
this.value.set(value);
}
#Override
public String toString() {
return "TrainingplanExercise{" + "exercise=" + exercise + ", training=" + training + ", value=" + value + '}';
}
}
class TrainingplanExerciseId implements Serializable{
protected ObjectProperty<Exercise> exercise;
protected ObjectProperty<Training> training;
protected ObjectProperty<Parameter> parameter;
public TrainingplanExerciseId() {
if(exercise == null)
exercise = new SimpleObjectProperty<>(null);
if(training == null)
training = new SimpleObjectProperty<>(null);
if(parameter == null)
parameter = new SimpleObjectProperty<>(null);
}
public TrainingplanExerciseId(ObjectProperty<Exercise> exercise, ObjectProperty<Training> training, ObjectProperty<Parameter> parameter) {
this.exercise = exercise;
this.training = training;
this.parameter = parameter;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TrainingplanExerciseId that = (TrainingplanExerciseId) o;
if (exercise != null ? !exercise.equals(that.exercise) : that.exercise != null) return false;
if (training != null ? !training.equals(that.training) : that.training != null) return false;
return parameter != null ? parameter.equals(that.parameter) : that.parameter == null;
}
#Override
public int hashCode() {
int result = exercise != null ? exercise.hashCode() : 0;
result = 31 * result + (training != null ? training.hashCode() : 0);
result = 31 * result + (parameter != null ? parameter.hashCode() : 0);
return result;
}
public Exercise getExercise() {
return exercise.get();
}
public ObjectProperty<Exercise> exerciseProperty() {
return exercise;
}
public void setExercise(Exercise exercise) {
this.exercise.set(exercise);
}
public Training getTraining() {
return training.get();
}
public ObjectProperty<Training> trainingProperty() {
return training;
}
public void setTraining(Training training) {
this.training.set(training);
}
public Parameter getParameter() {
return parameter.get();
}
public ObjectProperty<Parameter> parameterProperty() {
return parameter;
}
public void setParameter(Parameter parameter) {
this.parameter.set(parameter);
}
}
Now when I want to save a new Training, I get this error:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'TrainingplanID' in 'field list'
Because of this SQL:
Hibernate: insert into TrainingplanExercises (TrainingplanID, trainingplan, exercise, parameter) values (?, ?, ?, ?)
How do I fix this?
If I change the joinColumn to "trainingplan" I get the error that there are two same columns. If I remove "trainingplan" from the reversed columns, I get an error that one is missing because the foreign constraint requires 3 columns
EDIT:
Try something from the comments. I did try OneToMany/ManyToOne:
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "trainingplan", nullable = false)
public Training getTraining() {
return training.get();
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "training")
public List<TrainingplanExercise> getExercises() {
return exercises;
}
If I try saving a training to the DB now, it works.
Let's say I want to get a Trainingplan from the database, and add new TrainingplanExercises. I would use this code:
Exercise ex = (Exercise) db.getAll(Exercise.class).get(1);
Training t = (Training) db.getAll(Training.class).get(0);
TrainingplanExercise te = new TrainingplanExercise(ex, t, "asdf", ex.getParameters().get(0));
TrainingplanExercise te1 = new TrainingplanExercise(ex, t, "asdf", ex.getParameters().get(1));
TrainingplanExercise te2 = new TrainingplanExercise(ex, t, "asdf", ex.getParameters().get(2));
TrainingplanExercise te3 = new TrainingplanExercise(ex, t, "asdf", ex.getParameters().get(3));
t.getExercises().clear();
t.getExercises().add(te);
t.getExercises().add(te1);
t.getExercises().add(te2);
t.getExercises().add(te3);
db.updateObj(t);
I get this exception:
Exception in thread "main" org.hibernate.exception.LockTimeoutException: could not execute statement
at org.hibernate.dialect.MySQLDialect$1.convert(MySQLDialect.java:447)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:211)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:62)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3124)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3581)
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:104)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1258)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
at db.Database.updateObj(Database.java:100)
at db.Database.main(Database.java:171)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:998)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3835)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3771)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2435)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2582)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2535)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1911)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2145)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2081)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2066)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208)
... 19 more
Okay, look. What you have is a design problem, not really a general problem. First, as I understand it, you want to make a set of unique TrainingplanExercise's. For that, you have this Entity:
#Entity
public class TrainingplanExercise implements Serializable {
#EmbeddedId private TrainingplanExerciseId trainingplanExerciseId;
public TrainingplanExercise() {}
public TrainingplanExercise(TrainingplanExerciseId trainingplanExerciseId) {
this.trainingplanExerciseId = trainingplanExerciseId;
}
... other fields ...
}
The difference between the above Entity and your original Entity is that I have made the ID an EmbeddableId. In order to insure that only unique exercises are put into the TrainingplanExercise's, you have a compositeKey that was defined as a separate class:
#Embeddable
public class TrainingplanExerciseId implements Serializable {
private String exercise;
private String parameter;
public TrainingplanExerciseId() {}
public TrainingplanExerciseId(String exercise, String parameter) {
this.exercise = exercise;
this.parameter = parameter;
}
... getters, setters, hashCode, and equals
}
Here, I have made the class Embeddable so that it can be used as an ID. The way you were trying to declare a compositeKey didn't make any sense; you were trying to declare each individual field in the TrainingplanExercise Entity as an ID, but you can only have one ID.
What is different in this model is that the TrainingplanExerciseId compositeKey does not include a reference back to a TrainingPlan. If you are trying to get a list of TrainingPlan's that use any specific TrainingplanExercise, then you would need a Bidirectional instead of a Unidirectional relationship, but that's a different issue. Otherwise, I don't know why you want to refer back to a TrainingPlan from a TrainingplanExercise. Further, you were putting a reference to the TrainingPlan into the TrainingplanExerciseId compositeKey, which would require the TrainingPlan to be serialized, which really wouldn't work as a unique Id.
Now you can put individual exercises into the table:
public TrainingplanExercise createExercise(String exercise, String parameter) {
TrainingplanExercise trainingplanExercise = new TrainingplanExercise(new TrainingplanExerciseId(exercise, parameter));
em.persist( trainingplanExercise );
return trainingplanExercise;
}
After that, you want to have any number of TrainingPlan's that use the possible TrainingplanExercise's, which you do with this Entity:
#Entity
public class TrainingPlan implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#ManyToMany(fetch=FetchType.EAGER)
private List<TrainingplanExercise> trainingplanExercises = new ArrayList<TrainingplanExercise>();
... getters, setters,
}
You have a ManyToMany relationship because a TrainingPlan refers to many TrainingplanExercise's and a TrainingplanExercise is used by many TrainingPlan's. You don't need any special annotation besides ManyToMany, the JPA provider will create a link table, putting the key from each Entity into a row, like this:
create table TrainingPlan_TrainingplanExercise (
TrainingPlan_id bigint not null,
trainingplanExercises_exercise varchar(255) not null,
trainingplanExercises_parameter varchar(255) not null
);
If you declare it as a OneToMany relationship, then the JPA provider will put an additional constraint on the link table insuring that a TrainingplanExercise cannot be linked to more than one TrainingPlan, so you don't want that. Just for example's sake, this is what the constraint would look like.
alter table TrainingPlan_TrainingplanExercise
add constraint UK_t0ku26ydvjkrme5ycrnlechgi unique (trainingplanExercises_exercise, trainingplanExercises_parameter);
Creating and updating TrainingPlans is straight forward:
public TrainingPlan createTrainingPlan() {
TrainingPlan trainingPlan = new TrainingPlan();
em.persist(trainingPlan);
return trainingPlan;
}
public TrainingPlan updateTrainingPlan(TrainingPlan trainingPlan) {
return em.merge(trainingPlan);
}
Now, you can create TrainingplanExercises and TrainingPlans, and add the exercises to the training plans and update them.
TrainingplanExercise squats20 = trainingService.createExercise("Squats", "20");
TrainingplanExercise lifts10 = trainingService.createExercise("Lifts", "10");
TrainingplanExercise crunches50 = trainingService.createExercise("Crunches", "50");
TrainingPlan trainingPlan = trainingService.createTrainingPlan();
trainingPlan.getTrainingplanExercises().add( squats20 );
trainingPlan.getTrainingplanExercises().add( lifts10 );
trainingService.updateTrainingPlan(trainingPlan);
trainingPlan = trainingService.createTrainingPlan();
trainingPlan.getTrainingplanExercises().add( lifts10 );
trainingPlan.getTrainingplanExercises().add( crunches50 );
trainingService.updateTrainingPlan(trainingPlan);
Also note that your application has the challenge of insuring that only unique TrainingplanExercises are created by users. If a TrainingplanExercise with a duplicate exercise and parameter is attempted to be created you will get a Unique index or primary key violation exception and the transaction will be rolled back.
EDIT: For reading the TrainingPlans, something like this can be used:
public List<TrainingPlan> listTrainingPlans() {
CriteriaQuery<TrainingPlan> criteria = em.getCriteriaBuilder().createQuery(TrainingPlan.class);
criteria.select(criteria.from(TrainingPlan.class));
List<TrainingPlan> trainingPlans = em.createQuery(criteria).getResultList();
return trainingPlans;
}
Note that since the List<TrainingplanExercise> trainingplanExercises is set to FetchType.EAGER this particular query will pull in the entire database. FetchType.EAGER probably isn't a problem for reading a single TrainingPlan, but if you only wanted a list of the TrainingPlan's without getting all of the details, then you would need to work out how FetchType.LAZY should be implemented.
Did you tried using many-to-one mapping instead because it's what you have with a foreign key anyway. You could then try something like:
#Id
#ManyToOne( cascade = {CascadeType.PERSIST}, targetEntity=Trainingplan.class )
#JoinColumn(name = "trainingplan")
public Training getTraining() {}

Cannot deploy the project, Caused by: Errors in named queries

I have a query. But it does not work. I tested it on Oracle SQL Developer, It worked. So I wrote it in namedquery.
The query is below that works fine on Oracle SQL Developer
SELECT q.* FROM TAKE_EXAM_QUESTION q
INNER JOIN TAKE_EXAM e
on q.tk_exam_id = e.tk_exam_id
where e.user_id= :userId;
And I typed above query in Entity class
#NamedQuery(name = "TakeExamQuestionEntity.question", query = "SELECT qs FROM TakeExamQuestionEntity qs INNER JOIN TakeExamEntity ex on qs.tk_exam_id = ex.tk_exam_id where ex.user_id= :userId "),
But it is not working, I do not know why now working please suggest me.
If you generate entity from table, any fields of table will be remove _ and first character after _ will be upper case. Thus, you should write NamedQuery as below example.
#NamedQuery(name = "TakeExamQuestionEntity.question",
query = "SELECT qs FROM TakeExamQuestionEntity qs
INNER JOIN TakeExamEntity ex on qs.tkExamId = ex.tkExamId
where ex.userId= :userId ")
If it not work, you should check you entity they are separated to 2 class (entity Primary Key and entity class) or not.
#Embeddable
public class EmployeePK implements Serializable {
private String name;
private long id;
public EmployeePK() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int hashCode() {
return (int) name.hashCode() + id;
}
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof EmployeePK)) return false;
if (obj == null) return false;
EmployeePK pk = (EmployeePK) obj;
return pk.id == id && pk.name.equals(name);
}
}
#Entity
public class Employee implements Serializable {
EmployeePK primaryKey;
public Employee() {
}
#EmbeddedId
public EmployeePK getPrimaryKey() {
return primaryKey;
}
public void setPrimaryKey(EmployeePK pk) {
primaryKey = pk;
}
...
}
If your entity has generated as 2 class as above example.
You should define
SELECT e FROM Employee e
where e.primaryKey.name=:name
the primaryKey will be an object of EmployeePK that has annotation Embeddable.
If you want to use native query you should use #NamedNativeQuery instead of #NamedQuery.
See also: NamedNativeQuery

Java EE 6, EJB 3.1, JPA 2.0 - error in inserting record in database

I am trying to insert a record in the database (using Java EE 6, EJB 3.1, JPA 2.0). I am getting an error that accountTypeId field is null, but i have set it up as autogenerate. Can anyone please suggest what am I doing wrong?
Following is the create table query:
create table example.account_type(
account_type_id INT NOT null PRIMARY KEY GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
account_type_desc varchar(20)
);
Following is the entity class:
EDIT: Updated the entity class as generated by NetBeans which didn't work. I also added #GeneratedValue annotation but still it didn't work.
#Entity
#Table(name = "ACCOUNT_TYPE")
#NamedQueries({
#NamedQuery(name = "AccountType.findAll", query = "SELECT a FROM AccountType a"),
#NamedQuery(name = "AccountType.findByAccountTypeId", query = "SELECT a FROM AccountType a WHERE a.accountTypeId = :accountTypeId"),
#NamedQuery(name = "AccountType.findByAccountTypeDesc", query = "SELECT a FROM AccountType a WHERE a.accountTypeDesc = :accountTypeDesc")})
public class AccountType implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY) // ADDED THIS LINE
#Basic(optional = false)
#NotNull
#Column(name = "ACCOUNT_TYPE_ID")
private Integer accountTypeId;
#Size(max = 50)
#Column(name = "ACCOUNT_TYPE_DESC")
private String accountTypeDesc;
public AccountType() {
}
public AccountType(Integer accountTypeId) {
this.accountTypeId = accountTypeId;
}
public Integer getAccountTypeId() {
return accountTypeId;
}
public void setAccountTypeId(Integer accountTypeId) {
this.accountTypeId = accountTypeId;
}
public String getAccountTypeDesc() {
return accountTypeDesc;
}
public void setAccountTypeDesc(String accountTypeDesc) {
this.accountTypeDesc = accountTypeDesc;
}
#Override
public int hashCode() {
int hash = 0;
hash += (accountTypeId != null ? accountTypeId.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof AccountType)) {
return false;
}
AccountType other = (AccountType) object;
if ((this.accountTypeId == null && other.accountTypeId != null) || (this.accountTypeId != null && !this.accountTypeId.equals(other.accountTypeId))) {
return false;
}
return true;
}
#Override
public String toString() {
return "Entities.AccountType[ accountTypeId=" + accountTypeId + " ]";
}
}
Following is the session bean interface:
#Remote
public interface AccountTypeSessionBeanRemote {
public void createAccountType();
public void createAccountType(String accDesc);
}
Following is the session bean implementation class:
#Stateless
public class AccountTypeSessionBean implements AccountTypeSessionBeanRemote {
#PersistenceContext(unitName="ExamplePU")
private EntityManager em;
#Override
public void createAccountType(String accDesc) {
AccountType emp = new AccountType(accDsc);
try {
this.em.persist(emp);
System.out.println("after persist");
} catch(Exception ex) {
System.out.println("ex: " + ex.getMessage());
}
}
}
Following is the Main class:
public class Main {
#EJB
private static AccountTypeSessionBeanRemote accountTypeSessionBean;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
accountTypeSessionBean.createAccountType("test");
}
}
Following is the error:
INFO: ex: Object: Entities.AccountType[ accountTypeId=null ] is not a known entity type.
You are not getting an error because of "accountTypeId field is null". As the error message says, the error occurs because "Entities.AccountType[ accountTypeId=null ] is not a known entity type".
The most likely reason is that AccountType is not annotated with #Entity. This problem is likely solved by adding it. Additionally it makes sense to use
#GeneratedValue(strategy = GenerationType.IDENTITY)
instead of AUTO. Auto means that the provider chooses a strategy based on the capabilities of the target database. According to the table definition it seems clear that the preferred strategy is IDENTITY.
I changed my create table query as following:
create table example.account_type(
account_type_id INT NOT null PRIMARY KEY,
account_type_desc varchar(20)
);
Then had to add the following line to the entity class (Netbeans doesn't add that):
#GeneratedValue(strategy = GenerationType.AUTO)

Categories