Ebean model mapping itself with #OneToMany - java

I'm currently creating a ebean model User which trying to map back to itself. Since the User and it's child uses the data structure, my idea is to reuse them without creating a new model / table.
#Entity
public class User extends Model {
#Id
private String id;
private String email;
#ManyToOne(cascade = CascadeType.PERSIST)
private User parent;
#ElementCollection(fetch = FetchType.LAZY)
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<User> childs = new ArrayList<>();
....
}
And the database schema looks like this:
CREATE TABLE user (
id CHAR(7),
email CHAR(255),
parent_id CHAR(255),
...
);
The mapping #ManyToOne from child to parent is working fine. But i get the error below when running the app with the #OneToMany mapping introduced:
javax.persistence.PersistenceException: Error on models.User.childs Can not find mappedBy property [user] in [models.User]
I suspect ebean was trying to look for user_id in user table, which in fact it was linked by parent_id as screen in the database structure.
Any idea if this even supported by ebean or is there anything i'm missing here?

Related

EntityManager tries to create existing entity

I'm creating an application that allows users to create hotel room reservations.
I have the entities room, user, reservation
These are my relationships set in entity class Reservation:
#Id
#GeneratedValue
private int id;
private Date reservationDate;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Room room;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private User user;
When I try to save a new Reservation object containing an existing User using EntityManager.persist(), I get a PSQLException saying user with "id=..." violates Unique-Constraint »user_pkey«, because that user already exists. How do I tell EntitiyManager to not try to create that User (and Room at a later point too) as it already exists?
You get that error because JPA cascade the persist operation down to user which is seen as a new entity.
To prevent this:
reservation.setUser(entityManager.getReference(User.class, user.getId()));
where getId must be replaced accordingly.
The above code assumes you are not interested in modifying the user. Otherwise, a merge operation is needed:
reservation.setUser(entityManager.merge(user));

Hibernate Unidirectional OneToMany Not Working with composite id

I've been bashing my head on my keyboard for two days trying to figure this out...
Some background: We have a data model set up with a Perl code base that runs straight native SQL statements to the database via ODBC. For certain reasons, we decided to rewrite the code in Java... I thought it would be a good idea to use Hibernate to define all of the mappings. We don't want to edit the data model.
For simplicity sake, I can express the problem with only part of our data model. We have the entities "Job","JobDatabase" and "JobTable".
Job has a PK of job_name. Database has a PK of job_name,name. Table has a PK of job_name,src_database_name,name. As you may expect, Job has a OneToMany relationship with JobDatabase, and Database has a OneToMany with JobTable.
For purposes of this test, I'm starting with empty tables and trying to create some sample data. I can insert a Job and a JobDatabase, but when I try to insert the JobTable, Hibernate throws an error. Or more accurately, that is where it complains. It doesn't start executing my code because it detects the mapping error. However, if I remove the association between JobDatabase and JobTable, it will insert all Job and JobDatabase records correctly with no errors.
Sample Classes (all fields have getters/setters... there are also many other fields):
#Entity
#Table(name="Job")
public class Job implements Serializable {
#Id
#Column(name="job_name",nullable = false)
private String jobName;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "job_name", referencedColumnName = "job_name")
private Set<JobDatabase> databases;
}
#Entity
#Table(name="JobDatabase")
public class JobDatabase implements Serializable {
#Id
#Column(name="job_name",nullable = false)
private String jobName;
#Id
#Column(name="name",nullable = false)
private String name;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "job_name", referencedColumnName = "job_name"),
#JoinColumn(name = "name", referencedColumnName = "src_database_name")
})
private Set<JobTable> tables;
}
#Entity
#Table(name="JobTable")
public class JobTable implements Serializable{
#Id
#Column(name="job_name",nullable = false)
private String jobName;
#Id
#Column(name="src_database_name",nullable = false)
private String srcDatabaseName;
#Id
#Column(name="name",nullable = false)
private String name;
}
The error:
Exception in thread "main" org.hibernate.MappingException: Unable to find column with logical name: src_database_name in JobDatabase
I keep getting this error. I do not understand why it is looking for the referenced column in the entity "owning" the mapping. src_database_name does indeed only exist in JobTable - it is referred to as "name" in JobDatabase. JobTable also has a "name" field, but it refers to the name of the Table.
You need to have src_database_name column in your JobDatabase table. Or you can change src_database_name to other column name.
For composite key reference column must be present in your source table.

org.hibernate.TransientObjectException if any many-to-one fields are null (or not selected in form)

I am using jpa with play framework.I have an entity JobseekerDetails
#Entity
public class JobseekerDetails {
#Id
#Column(name = "jobseekerDetails_id", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long jobseekerDetails_id;
#ManyToOne
#JoinColumn(name = "basicEducation_id")
private JobseekerFormBasicEducation basicEducation;
#ManyToOne
#JoinColumn(name = "masterEducation_id")
private JobseekerFormMasterEducation masterEducation;
#ManyToOne
#JoinColumn(name = "doctrateEducation_id")
private JobseekerFormDoctrateEducation doctrateEducation;
#ElementCollection
private List<String> certificateName =new ArrayList<String>();
#Column(length = 3000)
private String resume;
private Long experience;
private String skills;
private String resumePath;
#ManyToOne
#JoinColumn(name = "industry_id")
private JobseekerFormIndustry industry;
#ManyToOne
#JoinColumn(name = "functionalArea_id")
private JobseekerFormFunctionalArea functionalArea;
}
which have a many-to-one relation with other entities like JobseekerFormFunctionalArea , JobseekerFormIndustry etc.These entities have fixed value which is already saved in the database.
When JobseekerDetails is saved all its details should be saved with corresponding manytoone relation ids but donot save to Entity JobseekerFormFunctionalArea and JobseekerFormIndustry as they are predefined
My problem is that when i save(through my form) all the manytoone relation fields ids in JobseekerDetails it is saved properly but when submit my form without selecting any value in any manytoone relation fields For ex if i dont select anything in my fuctionalArea_id field it gives the below exception
org.hibernate.TransientObjectException:object references an unsaved transient instance - save the transient instance before flushing
If I select all the fields the details are saved but if I dint select anyone field which is mapped manytoone in my model then it gives above exception
But jpa automatically set nullable=true then why this happend
I have searched a bit and found that this problem can be solved by adding cascade .I added cacade type Merge but getting the above same exception.
I also tryed setting nullable=true but getting same error
On setting cascade = CascadeType.ALL and cascade = CascadeType.PERSIST I am getting below exception
PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist:
Any help would be appreciated
When you don't select any 'basicEducation' in the form and you are saving form then there is new object there with empty id. So you have:
jobseekerDetails.basicEducation = new JobseekerFormBasicEducation ()
This causes TransientObjectException
To avoid it you have to set
jobseekerDetails.basicEducation=null
before saving.

JPA fetching too many entity class objects even when it is not rqquired

I have been working on a Enquiry ( ticket raising ) application.
But the application performance is low.
Here i had two requirements (overview page,detail page) in my application.
Whenever we open the application it has to show open tickets(enquireis) currently assigned to that user in a overview page.
The overview page has pagination for 10 records.
Here each record has a link to look into its details ( detail page).
We are using JPA and wicket technologies.
Here whenever we open the application,
the overview page has to show few fields data that have been mapped to few fields in five entity classes.
Here the main entity class is Enquiry which has other entities mapped to it like User,
Company,
Dealer,
Issue,
EnquiryText.
So when i open my application it needs few fields data in all these entities.
But JPA is loading all fields in all entities that are mapped to Enquiry class.
The worst part is other entities in Enquiry entity also mapped to some other entities.
Select enquiry.,enquiry.user.*,enquiry.company.*,
enquiry.issue.*,enquiry.dealer.*,enquiry.enquiryText.*
from
Enquiry enquiry
where ( enquiry.* = input value, enquiry.issue.* = input value )
I also had other requirement that when i click on each record in overview page it should fetch all the details of that record.
So it has to fetch all the entities in this case.
Select enquiry
from Enquiry enquiry
where ( enquiry.* = input value, enquiry.issue.* = input value )
Is it possible for us to make our application to stop fetching all entities
but fetch few entity fields in one requirement(while showing overview page)
and to make it fetch all entity fields in other requirement(when we click on each record i.e ticket) ?
#Entity
#Table(name = "ENQRY")
public class Enquiry extends AbstractEntity {
#Id
#Column(name = "CONT")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "EMAILADRADD")
private String emailAddresses = "";
#Column(name = "CONFIHME")
private String Comments = "";
#Column(name = "STCONT")
private DateTime lastStatusChangeOn = new DateTime();
#Column(name = "CLANGOPTR")
private String operatorLanguage;
#Column(name = "CLANGISS")
private String issuerLanguage;
#ManyToOne
#JoinColumn(name = "USERLUPD")
private User lastUpdateByUser;
#ManyToOne
#JoinColumn(name = "USERCREAT")
private User createdByUser;
#ManyToOne
#JoinColumn(name = "USERISS")
private User issuer;
#ManyToOne
#JoinColumn(name = "CMPY")
private Company company;
#OneToMany(cascade = { CascadeType.ALL }, mappedBy = "enquiry", fetch = FetchType.EAGER)
#Where(clause = "CTEXT = 'DIAGN'")
private List<EnquiryText> diagnosis;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "enquiry")
private List<EnquiryText> texts;
There is not a single solution to this problem.. two that I have in mind are:
Create a db view and then map a custom entity to this view (remember that views are readonly)
Create a DTO class and then make a select new dtopackege.myDto(e.field, e.field1, e.field2, e.field3.subfield) from MyEntity e
Your DTO must have a constructor capable of initialize all parameters passed by the query.
Then based on your requirement you can chose to invoke your custom methods or simply the hibernate plain ones that will fetch all properties.
Hope this helps!

JPA Mapping Issue - Modelling direction requested

I'm having a problem modelling the following problem in JPA. I have a JPA Entity class 'User', like so:
(accessors/mutators/extraneous fields/extraneous JPA configuration omitted for brevity)
#Entity
class User {
#Id
#Generated Value
long id;
#OneToMany
Report contributorReports; // All the reports where this user is contributor
#OneToMany ownerReports; // All the reports where this user is owner
String username;
}
and a JPA Entity class 'Report'
#Entity
class Report {
#Id
#GeneratedValue
long id;
#OneToOne
User contributor;
#OneToOne
User owner;
SomeData data;
}
I would like to model a relationship such that:
A Report must contain both a contributor and an owner
I can access all of the Reports a User has been a 'contributor' for via the User Entity
I can access all of the Reports a User has been a 'owner' for via the User Entity
I imagined I would end up with a mapping table that looked vaguely like this:
CREATE TABLE user_report {
BIGINT reportId,
BIGINT contributorId,
BIGINT ownerId,
}
I attempted to solve the problem like:
#OneToOne
#JoinTable(name = "user_report",
joinColumns = {
#JoinColumn(name = "reportOwner_ID", referencedColumnName = "ID")}
)
private User owner;
#OneToOne
#JoinTable(name = "user_report",
joinColumns = {
#JoinColumn(name = "reportContributor_ID", referencedColumnName = "ID")}
)
private User contributor;
This generates a table like:
CREATE TABLE user_report (
BIGINT ownerReport_ID, // Report ID
BIGINT reportOwner_ID, // UserID owner
BIGINT contributorReport_ID, // Report ID
BIGINT reportContributor_ID // UserID contributor
)
So when JPA attempts to map to this table, it maps each field separately and fails because only half of the row is being committed, throwing this exception:
Caused by: java.sql.BatchUpdateException: Field 'ownerReport_ID' doesn't have a default value
I was hoping to get some direction on how to best model the relationship I envision. (Or maybe a better way to envision the relationship) If additional information is needed, I'll be glad to provide it.
Kind Regards
Matt
Based on your requirements I believe you accomplish this with 2 1:M from User to Report with a matched M:1 back for each.
#Entity
class User {
#Id
#Generated Value
long id;
// All the reports where this user is contributor
#OneToMany(mappedBy="contributor")
List<Report> contributorReports;
// All the reports where this user is owner
#OneToMany(mappedBy="owner")
List<Report> ownerReports;
String username;
}
Then your Report class would look like:
#Entity
class Report {
#Id
#GeneratedValue
long id;
#ManyToOne
User contributor;
#ManyToOne
User owner;
SomeData data;
}
This situation is also possible with a join table but not required based on your requirements as I understand them.
Doug

Categories