how to map Many to Many with composite key - java

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() {}

Related

Primary and foreign key implementation Java and Android Room

This is my first time asking a question here in StackOverflow so forgive me if I am not asking the question right or a certain way.
I have two child classes (PerformanceAssessment and ObjectiveAssessment) from parent class Assessment. I was able to successfully do downcasting for the polymorphism part requirement of my school project, and I was able to save to their appropriate databases both instances of parent and child after downcasting (I have an assessment_table, performance_assessments, and objective_assessments table). However, I am now having Android Room database issues. I realized that I need to implement foreign keys to do the CRUD operations properly. How do I tweak my parent and child classes code and add proper annotations for implementing foreign and primary keys correctly? I need to have an autogenerated primary key for each class: parent (Assessment) and both children (PerformanceAssessment and ObjectiveAssessment). But I also need to utilize the children's primary key as a foreign key for the parent's databases so that when I delete an instance of Performance/Objective assessments, I can also delete the Assessment instance when I am downcasting. I found very little helpful information regarding this. Thanks in advance.
I am getting these errors right now:
Build Errors
Parent Class: Assessment.java
#Entity(tableName = "assessment_table")
public class Assessment {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "assessment_id")
private final int assessment_id;
private String assessmentName;
private String assessmentStart;
private String assessmentEnd;
private int courseID;
public Assessment(int assessment_id, String assessmentName, String assessmentStart, String assessmentEnd, int courseID) {
this.assessment_id = assessment_id;
this.assessmentName = assessmentName;
this.assessmentStart = assessmentStart;
this.assessmentEnd = assessmentEnd;
this.courseID = courseID;
}
Child class: PerformanceAssessment.java
#Entity(tableName = "performance_assessment", foreignKeys = {
#ForeignKey(
entity = Assessment.class,
parentColumns = "assessment_id",
childColumns = "performance_id",
onUpdate = CASCADE,
onDelete = CASCADE
)
})
public class PerformanceAssessment extends Assessment{
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "performance_id")
private int performanceID;
private String type;
public PerformanceAssessment(int assessment_id, String assessmentName, String assessmentStart, String assessmentEnd, int courseID, int performanceID, String type) {
super(assessment_id, assessmentName, assessmentStart, assessmentEnd, courseID);
this.performanceID = performanceID;
this.type = type;
}
Child class: ObjectiveAssessment.java
#Entity(tableName = "objective_assessment", foreignKeys = {
#ForeignKey(
entity = Assessment.class,
parentColumns = "assessment_id",
childColumns = "objective_id",
onUpdate = CASCADE,
onDelete = CASCADE
)
})
public class ObjectiveAssessment extends Assessment{
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "objective_id")
private int objective_ID;
private String type;
public ObjectiveAssessment(int assessmentID, String assessmentName, String assessmentStart, String assessmentEnd, int courseID, int objective_ID, String type) {
super(assessmentID, assessmentName, assessmentStart, assessmentEnd, courseID);
this.objective_ID = objective_ID;
this.type = type;
}
Here is the part where I am adding a new assessment in the app:
public void saveAssessment(View view) {
assessmentTitle = editName.getText().toString();
assessmentStart = editStart.getText().toString();
assessmentEnd = editEnd.getText().toString();
//Check if fields are empty:
if (assessmentTitle.isEmpty() || assessmentStart.isEmpty() || assessmentEnd.isEmpty()) {
Toast.makeText(AddAssessmentScreen.this, "Fill out required fields.", Toast.LENGTH_LONG).show();
return;
}
else {
//Check assessment type selected (used Downcasting for polymorphism):
if (assessment_type == true) {
Assessment performanceAssessment = new PerformanceAssessment(0, assessmentTitle, assessmentStart, assessmentEnd, currentCourseID, 0, selectedString);
PerformanceAssessment castedPerformance = (PerformanceAssessment) performanceAssessment;
//Insert assessment to database performance_assessment table (Performance child type):
repository.insert(castedPerformance);
Repository addToAssessment = new Repository(getApplication());
//Insert assessment to database assessment_table (Assessment parent type):
addToAssessment.insert(performanceAssessment);
}
else {
Assessment objectiveAssessment = new ObjectiveAssessment(0,assessmentTitle, assessmentStart, assessmentEnd, currentCourseID, 0, selectedString);
ObjectiveAssessment castedObjective = (ObjectiveAssessment) objectiveAssessment;
//Insert assessment to database objective_assessment table (Objective child type):
repository.insert(castedObjective);
Repository addToAssessment = new Repository(getApplication());
//Insert assessment to database assessment_table (Assessment parent type):
addToAssessment.insert(objectiveAssessment);
}
Toast.makeText(AddAssessmentScreen.this, "New assessment added. Refresh previous screen.", Toast.LENGTH_LONG).show();
}
How do I tweak my parent and child classes code and add proper annotations for implementing foreign and primary keys correctly?
Let the assessment_id always be the primary key i.e. don't code it in sub classes and then have a field for the reference/map/association with the parent in the subclasses.
So PerformanceAssessment could be :-
#Entity(tableName = "performance_assessment", foreignKeys = {
#ForeignKey(
entity = Assessment.class,
parentColumns = "assessment_id",
childColumns = "assessment_reference",
onUpdate = CASCADE,
onDelete = CASCADE
)
})
public class PerformanceAssessment extends Assessment {
private int assessment_reference; //<<<<<<<<<
private String type;
public PerformanceAssessment(int assessment_id, String assessmentName, String assessmentStart, String assessmentEnd, int courseID, int assessment_reference, String type) {
super(assessment_id, assessmentName, assessmentStart, assessmentEnd, courseID);
this.assessment_reference = assessment_reference;
this.type = type;
}
....
and all compiles and the underlying tables will be build using :-
_db.execSQL("CREATE TABLE IF NOT EXISTS `assessment_table` (`assessment_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `assessmentName` TEXT, `assessmentStart` TEXT, `assessmentEnd` TEXT, `courseID` INTEGER NOT NULL)");
_db.execSQL("CREATE TABLE IF NOT EXISTS `performance_assessment` (`assessment_reference` INTEGER NOT NULL, `type` TEXT, `assessment_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `assessmentName` TEXT, `assessmentStart` TEXT, `assessmentEnd` TEXT, `courseID` INTEGER NOT NULL, FOREIGN KEY(`assessment_reference`) REFERENCES `assessment_table`(`assessment_id`) ON UPDATE CASCADE ON DELETE CASCADE )");
Obviously similar for the ObjectiveAssessment.
But I also need to utilize the children's primary key as a foreign key for the parent's databases so that when I delete an instance of Performance/Objective assessments, I can also delete the Assessment instance when I am downcasting.
What if, which is possible with the 1 (Assessment) - Many (PerformanceAssessments), an Assessment has multiple PerformanceAssesments? Deleting 1 PerformanceAsessment would delete the parent Assessment, which would then, due to the use of onDelete CASCADE cascade the deletions to all the other PerformanceAssessments and ObjectiveAssessments. Deletions are not propagated up to the parent (that's why the term CASCADE is used as it implies downward rather than anyway).
As an example you have an assessment with 4 PerformanceAssessments and lets say 3 ObjectiveAssessments one of the PerformanceAssesments was wrongly added. Should everything have to be deleted and re-entered to correct the 1 wrong PeformanceAssessment.
You could introduce a Trigger to automate this upwards propogation, to delete anything and delete all, if that is what you want.
Additional Re comments
I have a one to one relationship for the Assessment and Performance/Objective assessment. One assessment can only have either performance or objective type.
That may be what you wish BUT there is nothing stopping an Assessment having multiple Performance and or Objectives. That may or may not be an issue.
Also, instead of downcasting, I did upcasting instead on where I save the assessment in the app. I ran into Foreign Key Constraint Failed (code 787) and realized it was because I was downcasting. My only dilemma now is that whenever I am saving an assessment, it will only save on assessment_table and not on performance_assessment or objective_assessment tbl.
What you have to do is insert the Assessment (parent) and then use the parent's id to insert the related Peformance/Objective with the assessment_reference set the the value of the assessment.
The 787 is it saying that you cannot insert a child (Performance/Objective) without the assessment_reference being an assessment_id in the Assessment.
What you can do, which would very likely suit your situation is a) have an #Insert long insert(Assessment assessment); and an #Insert long (PerformanceAssessment performanceAssessment) (likewise for Objective)
and then use (assuming dao is an instance of the #Dao)
and use
dao.insert(new PerformanceAssessment(0,"the title", etc,dao.insert(Assessment(....),"the type");
So the Assessment is added as part of inserting the performance/Objective and with the id of the assessment being used as the assessment_reference value.
NOTE the #Insert will only return a long, so you have to convert this to an int. I would suggest changing id's and references to be long's. The overhead, if any, will have a minimal impact. Storage in the database will not be affected and it will save the hassle of converting long to int.
Demonstration
The following is a demonstration based upon code that closely reflects your but with some subtle changes.
id's have been changed to be long rather than int.
autogenerate has been removed BUT the id's will be generated as before.
2. All that autogenerate does from an SQLite standpoint is include the AUTOINCREMENT key word. This is actually inefficient and NOT recommended. https://sqlite.org/autoinc.html
So the code is :-
Assessment
#Entity(tableName = "assessment_table")
public class Assessment {
#PrimaryKey /* no need for autogenerate */
#ColumnInfo(name = "assessment_id")
/* As not autogenerate then use Long and default to null */
/* same as autogenerate but without the overheads/inefficiencies */
private Long assessment_id = null; //Long rather than long so can be null and have id generated.
private String assessmentName;
private String assessmentStart;
private String assessmentEnd;
private long courseID;
public Assessment(long assessment_id, String assessmentName, String assessmentStart, String assessmentEnd, long courseID) {
this.assessment_id = assessment_id;
this.assessmentName = assessmentName;
this.assessmentStart = assessmentStart;
this.assessmentEnd = assessmentEnd;
this.courseID = courseID;
}
#Ignore
/* Alternative constructor no need to provide id for inserting */
public Assessment(String assessmentName, String assessmentStart, String assessmentEnd, long courseID) {
this.assessmentName = assessmentName;
this.assessmentStart = assessmentStart;
this.assessmentEnd = assessmentEnd;
this.courseID = courseID;
}
public Long getAssessment_id() {
return assessment_id;
}
public void setAssessment_id(Long assessment_id) {
this.assessment_id = assessment_id;
}
public String getAssessmentName() {
return assessmentName;
}
public void setAssessmentName(String assessmentName) {
this.assessmentName = assessmentName;
}
public String getAssessmentStart() {
return assessmentStart;
}
public void setAssessmentStart(String assessmentStart) {
this.assessmentStart = assessmentStart;
}
public String getAssessmentEnd() {
return assessmentEnd;
}
public void setAssessmentEnd(String assessmentEnd) {
this.assessmentEnd = assessmentEnd;
}
public long getCourseID() {
return courseID;
}
public void setCourseID(long courseID) {
this.courseID = courseID;
}
}
PerformanceAssessment
#Entity(tableName = "performance_assessment", foreignKeys = {
#ForeignKey(
entity = Assessment.class,
parentColumns = "assessment_id",
childColumns = "assessment_reference",
onUpdate = CASCADE,
onDelete = CASCADE
)
})
public class PerformanceAssessment extends Assessment {
private long assessment_reference;
private String type;
public PerformanceAssessment(long assessment_id, String assessmentName, String assessmentStart, String assessmentEnd, long courseID, long assessment_reference, String type) {
super(assessment_id, assessmentName, assessmentStart, assessmentEnd, courseID);
this.assessment_reference = assessment_reference;
this.type = type;
}
#Ignore
public PerformanceAssessment(String assessmentName, String assessmentStart, String assessmentEnd, long courseID, long assessment_reference, String type) {
super(assessmentName, assessmentStart, assessmentEnd, courseID);
this.assessment_reference = assessment_reference;
this.type = type;
}
#Override
public Long getAssessment_id() {
return super.getAssessment_id();
}
#Override
public void setAssessment_id(Long assessment_id) {
super.setAssessment_id(assessment_id);
}
#Override
public String getAssessmentName() {
return super.getAssessmentName();
}
#Override
public void setAssessmentName(String assessmentName) {
super.setAssessmentName(assessmentName);
}
#Override
public String getAssessmentStart() {
return super.getAssessmentStart();
}
#Override
public void setAssessmentStart(String assessmentStart) {
super.setAssessmentStart(assessmentStart);
}
#Override
public String getAssessmentEnd() {
return super.getAssessmentEnd();
}
#Override
public void setAssessmentEnd(String assessmentEnd) {
super.setAssessmentEnd(assessmentEnd);
}
#Override
public long getCourseID() {
return super.getCourseID();
}
#Override
public void setCourseID(long courseID) {
super.setCourseID(courseID);
}
public long getAssessment_reference() {
return assessment_reference;
}
public void setAssessment_reference(long assessment_reference) {
this.assessment_reference = assessment_reference;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
ObjectiveAssessment
#Entity(tableName = "objective_assessment", foreignKeys = {
#ForeignKey(
entity = Assessment.class,
parentColumns = "assessment_id",
childColumns = "assessment_reference",
onUpdate = CASCADE,
onDelete = CASCADE
)
})
public class ObjectiveAssessment extends Assessment {
private long assessment_reference;
private String type;
public ObjectiveAssessment(long assessment_id, String assessmentName, String assessmentStart, String assessmentEnd, long courseID, long assessment_reference, String type) {
super(assessment_id, assessmentName, assessmentStart, assessmentEnd, courseID);
this.assessment_reference = assessment_reference;
this.type = type;
}
#Ignore
public ObjectiveAssessment(String assessmentName, String assessmentStart, String assessmentEnd, long courseID, long assessment_reference, String type) {
super(assessmentName, assessmentStart, assessmentEnd, courseID);
this.assessment_reference = assessment_reference;
this.type = type;
}
#Override
public Long getAssessment_id() {
return super.getAssessment_id();
}
#Override
public void setAssessment_id(Long assessment_id) {
super.setAssessment_id(assessment_id);
}
#Override
public String getAssessmentName() {
return super.getAssessmentName();
}
#Override
public void setAssessmentName(String assessmentName) {
super.setAssessmentName(assessmentName);
}
#Override
public String getAssessmentStart() {
return super.getAssessmentStart();
}
#Override
public void setAssessmentStart(String assessmentStart) {
super.setAssessmentStart(assessmentStart);
}
#Override
public String getAssessmentEnd() {
return super.getAssessmentEnd();
}
#Override
public void setAssessmentEnd(String assessmentEnd) {
super.setAssessmentEnd(assessmentEnd);
}
#Override
public long getCourseID() {
return super.getCourseID();
}
#Override
public void setCourseID(long courseID) {
super.setCourseID(courseID);
}
public long getAssessment_reference() {
return assessment_reference;
}
public void setAssessment_reference(long assessment_reference) {
this.assessment_reference = assessment_reference;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
A POJO has the Assessment Embedded and the Perf and Obj included via #Relation (not the ideal as if the rule of only 1 or the other than 1 will be null). So :-
AssessmentWithPerformanceAssessmentAndObjectiveAssessment
class AssessmentWithPerformanceAssessmentAndObjectiveAssessment {
#Embedded
Assessment assessment;
#Relation(
entity = PerformanceAssessment.class,
parentColumn = "assessment_id",
entityColumn = "assessment_reference"
)
PerformanceAssessment performanceAssessment;
#Relation(
entity = ObjectiveAssessment.class,
parentColumn = "assessment_id",
entityColumn = "assessment_reference"
)
ObjectiveAssessment objectiveAssessment;
}
A single #Dao annotated class :-
AssessmentDao
#Dao
abstract class AssessmentDao {
#Insert
abstract long insert(Assessment assessment);
#Insert
abstract long insert(PerformanceAssessment performanceAssessment);
#Insert
abstract long insert(ObjectiveAssessment objectiveAssessment);
/*
use to check if an Assessment has any children
*/
#Query("WITH cte_counts(counter) AS (" +
"SELECT count(*) > 0 FROM performance_assessment WHERE assessment_reference=:assessment_id " +
"UNION ALL SELECT count(*) FROM objective_assessment WHERE assessment_reference=:assessment_id" +
")" +
"SELECT sum(counter) > 0 FROM cte_counts")
abstract boolean hasChildAlready(long assessment_id);
#Transaction
#Query("SELECT * FROM assessment_table WHERE assessment_id=:assessment_id")
abstract AssessmentWithPerformanceAssessmentAndObjectiveAssessment getAssessmentWithPerformanceAssessmentOrObjectiveAssessmentById(long assessment_id);
#Transaction
#Query("SELECT * FROM assessment_table")
abstract List<AssessmentWithPerformanceAssessmentAndObjectiveAssessment> getAllAssessmentsWithPerformanceAssessmentOrObjectiveAssessmentById();
}
note an abstract class rather than an interface (could be an interface though)
An #Database annotated class
AssessmentDatabase
#Database(entities = {Assessment.class,PerformanceAssessment.class,ObjectiveAssessment.class},version =1)
abstract class AssessmentDatabase extends RoomDatabase {
abstract AssessmentDao getAssessmentDao();
private static volatile AssessmentDatabase instance = null;
static AssessmentDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context,AssessmentDatabase.class,"assessment.db")
.allowMainThreadQueries()
.build();
}
return instance;
}
}
note for convenience and brevity allows running on the main thread.
Finally actually using the above in an Activity:-
public class MainActivity extends AppCompatActivity {
AssessmentDatabase db;
AssessmentDao dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = AssessmentDatabase.getInstance(this);
dao = db.getAssessmentDao();
/* Various Inserts */
long loneAssessment = dao.insert(
new Assessment(
"Lone Assessment with no children",
"2021-01-15",
"2021-03-15",
0L
)
);
/* id will be 100, subsequent id's if generated will be 101,102......*/
long a1 = dao.insert(new Assessment(100,"A1","2022-01-01","2022,03-01",10));
long p1 = dao.insert(new PerformanceAssessment("P1","2022-01-31","2022-01-31",10,a1,"X"));
/* back to using generated Assessment id */
long a2 = dao.insert(new Assessment("A1","2022-01-01","2022-03-01",11));
long p2 = dao.insert(new ObjectiveAssessment("O1","2022-01-31","2022-01-31",11,a2,"Y"));
/* Build the Assessment and ObjectiveAssessment ready for insert SEE WARNING */
Assessment a10 = new Assessment("A10","2022-01-01","2022-03-01",20);
ObjectiveAssessment o10 = new ObjectiveAssessment("O10","2022-01-31","2022-01-31",a10.getCourseID(),0,"Z");
/* WARNING assessment_reference WILL NOT BE ANY GOOD (will be 0) */
long a10id = dao.insert(a10);
o10.setAssessment_reference(a10id); /*<<<<<<<<<< NOW assessment_reference should be good */
dao.insert(o10);
/* Both together */
dao.insert(
new PerformanceAssessment("P20","2022-05-07","2022-05-07",11,
/* get the Assessment ID from the insert of the Assessment */
dao.insert(
new Assessment("A20","2022-04-01","2022-06-17",21)
),
"ZZ"
)
);
/* Extract all the Assessments with their children (if any)*/
for(AssessmentWithPerformanceAssessmentAndObjectiveAssessment awpoo: dao.getAllAssessmentsWithPerformanceAssessmentOrObjectiveAssessmentById()) {
Log.d("DBINFO","Assessment is " + awpoo.assessment.getAssessmentName() + " id is " + awpoo.assessment.getAssessment_id() + " etc....");
/* need to check if the the performanceassessment is null - it will be if there isn't one */
if (awpoo.performanceAssessment != null) {
Log.d("DBINFO","\t\tPerformanceAssessment = " + awpoo.performanceAssessment.getAssessmentName() +
" id is "+ awpoo.performanceAssessment.getAssessment_id() +
" references Asessment with id " + awpoo.performanceAssessment.getAssessment_reference());
} else {
Log.d("DBINFO","\t\tNo PerformanceAssessment.");
}
/* null check see performance assessment check above */
if (awpoo.objectiveAssessment != null) {
Log.d("DBINFO","\t\tObjectiveAssessment = " + awpoo.objectiveAssessment.getAssessmentName() +
" id is "+ awpoo.objectiveAssessment.getAssessment_id() +
" references Asessment with id " + awpoo.objectiveAssessment.getAssessment_reference());
} else {
Log.d("DBINFO","\t\tNo ObjectiveAssessment.");
}
}
}
}
When run (first time, will fail if run twice due to use of 100 for the id) the log shows :-
D/DBINFO: Assessment is Lone Assessment with no children id is 1 etc....
D/DBINFO: No PerformanceAssessment.
D/DBINFO: No ObjectiveAssessment.
D/DBINFO: Assessment is A1 id is 100 etc....
D/DBINFO: PerformanceAssessment = P1 id is 1 references Asessment with id 100
D/DBINFO: No ObjectiveAssessment.
D/DBINFO: Assessment is A1 id is 101 etc....
D/DBINFO: No PerformanceAssessment.
D/DBINFO: ObjectiveAssessment = O1 id is 1 references Asessment with id 101
D/DBINFO: Assessment is A10 id is 102 etc....
D/DBINFO: No PerformanceAssessment.
D/DBINFO: ObjectiveAssessment = O10 id is 2 references Asessment with id 102
D/DBINFO: Assessment is A20 id is 103 etc....
D/DBINFO: PerformanceAssessment = P20 id is 2 references Asessment with id 103
D/DBINFO: No ObjectiveAssessment.
using App Inspection then the database looks like :-
See how PerformanceAssessment with an assessment_id of 2 (it's unqiue row identifier) has an assessment_reference value of 103. This says the the Assessment with an assessment_id of 103 is the parent (or this PerformanceAssessment relates(belongs) to the Assessment that has an assessment_id of 103).

How to fix missing descriptor for class POJO after update server? [duplicate]

I'm using EclipseLink to run some Native SQL. I need to return the data into a POJO. I followed the instructions at EclipseLink Docs, but I receive the error Missing descriptor for [Class]
The query columns have been named to match the member variables of the POJO. Do I need to do some additional mapping?
POJO:
public class AnnouncementRecipientsFlattenedDTO {
private BigDecimal announcementId;
private String recipientAddress;
private String type;
public AnnouncementRecipientsFlattenedDTO() {
super();
}
public AnnouncementRecipientsFlattenedDTO(BigDecimal announcementId, String recipientAddress, String type) {
super();
this.announcementId = announcementId;
this.recipientAddress = recipientAddress;
this.type = type;
}
... Getters/Setters
Entity Manager call:
public List<AnnouncementRecipientsFlattenedDTO> getNormalizedRecipientsForAnnouncement(int announcementId) {
Query query = em.createNamedQuery(AnnouncementDeliveryLog.FIND_NORMALIZED_RECIPIENTS_FOR_ANNOUNCEMENT, AnnouncementRecipientsFlattenedDTO.class);
query.setParameter(1, announcementId);
return query.getResultList();
}
I found out you can put the results of a Native Query execution into a List of Arrays that hold Objects. Then one can iterate over the list and Array elements and build the desired Entity objects.
List<Object[]> rawResultList;
Query query =
em.createNamedQuery(AnnouncementDeliveryLog.FIND_NORMALIZED_RECIPIENTS_FOR_ANNOUNCEMENT);
rawResultList = query.getResultList();
for (Object[] resultElement : rawResultList) {
AnnouncementDeliveryLog adl = new AnnouncementDeliveryLog(getAnnouncementById(announcementId), (String)resultElement[1], (String)resultElement[2], "TO_SEND");
persistAnnouncementDeliveryLog(adl);
}
You can only use native SQL queries with a class if the class is mapped. You need to define the AnnouncementRecipientsFlattenedDTO class as an #Entity.
Otherwise just create the native query with only the SQL and get an array of the data back and construct your DTO yourself using the data.
Old question but may be following solution will help someone else.
Suppose you want to return a list of columns, data type and data length for a given table in Oracle. I have written below a native sample query for this:
private static final String TABLE_COLUMNS = "select utc.COLUMN_NAME, utc.DATA_TYPE, utc.DATA_LENGTH "
+ "from user_tab_columns utc "
+ "where utc.table_name = ? "
+ "order by utc.column_name asc";
Now the requirement is to construct a list of POJO from the result of above query.
Define TableColumn entity class as below:
#Entity
public class TableColumn implements Serializable {
#Id
#Column(name = "COLUMN_NAME")
private String columnName;
#Column(name = "DATA_TYPE")
private String dataType;
#Column(name = "DATA_LENGTH")
private int dataLength;
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public int getDataLength() {
return dataLength;
}
public void setDataLength(int dataLength) {
this.dataLength = dataLength;
}
public TableColumn(String columnName, String dataType, int dataLength) {
this.columnName = columnName;
this.dataType = dataType;
this.dataLength = dataLength;
}
public TableColumn(String columnName) {
this.columnName = columnName;
}
public TableColumn() {
}
#Override
public int hashCode() {
int hash = 0;
hash += (columnName != null ? columnName.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
if (!(object instanceof TableColumn)) {
return false;
}
TableColumn other = (TableColumn) object;
if ((this.columnName == null && other.columnName != null) || (this.columnName != null && !this.columnName.equals(other.columnName))) {
return false;
}
return true;
}
#Override
public String toString() {
return getColumnName();
}
}
Now we are ready to construct a list of POJO. Use the sample code below to construct get your result as List of POJOs.
public List<TableColumn> findTableColumns(String table) {
List<TableColumn> listTables = new ArrayList<>();
EntityManager em = emf.createEntityManager();
Query q = em.createNativeQuery(TABLE_COLUMNS, TableColumn.class).setParameter(1, table);
listTables = q.getResultList();
em.close();
return listTables;
}
Also, don't forget to add in your POJO class in persistence.xml! It can be easy to overlook if you are used to your IDE managing that file for you.
Had the same kind of problem where I wanted to return a List of POJOs, and really just POJOs (call it DTO if you want) and not #Entity annotated Objects.
class PojoExample {
String name;
#Enumerated(EnumType.STRING)
SomeEnum type;
public PojoExample(String name, SomeEnum type) {
this.name = name;
this.type = type;
}
}
With the following Query:
String query = "SELECT b.name, a.newtype as type FROM tablea a, tableb b where a.tableb_id = b_id";
Query query = getEntityManager().createNativeQuery(query, "PojoExample");
#SuppressWarnings("unchecked")
List<PojoExample> data = query.getResultList();
Creates the PojoExample from the database without the need for an Entity annotation on PojoExample. You can find the method call in the Oracle Docs here.
edit:
As it turns out you have to use #SqlResultSetMapping for this to work, otherwise your query.getResultList() returns a List of Object.
#SqlResultSetMapping(name = "PojoExample",
classes = #ConstructorResult(columns = {
#ColumnResult(name = "name", type = String.class),
#ColumnResult(name = "type", type = String.class)
},
targetClass = PojoExample.class)
)
Just put this anywhere under your #Entity annotation (so in this example either in tablea or tableb because PojoExample has no #Entity annotation)

How to remove an entity without loading its contents?

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)

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!

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

Categories