Hibernate inserts duplicates into one-to-many collection - eager fetch - java

I have two classes, monitoringExpression and reportTrigger, with a one-to-many relationship. Hibernate is attempting to persist duplicate reportTriggers from the collection held by the monitoringExpression class. The first insert into the reportTrigger collection works, but subsequent inserts fail with a unique constraint violation because hibernate tries to persist the same reportTrigger twice. This is quite similar to a known hibernate bug (Hibernate inserts duplicates into a #OneToMany collection); however, in this case, we are not using a lazy collection. Here is the relevant code:
MonitoringExpression.Class
#Audited
#Entity
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class MonitoringExpression extends GeneratedIdXmlObject{
private String name;
private DeterminantDefinition determinantDefinition;
private String valueExpression;
private String testExpression;
private String messageExpression;
private String messageSeverity;
private boolean setExitStatusWhenTrue;
protected SortedSet<MonitoringExpressionAttribute> attributes = new TreeSet<MonitoringExpressionAttribute>();
private String color;
private Set<ReportTrigger> reportTriggers = new HashSet<ReportTrigger>();
.
.
.
#OneToMany(mappedBy="monitoringExpression",orphanRemoval=true,cascade={CascadeType.ALL},fetch=FetchType.EAGER)
#Sort(type=SortType.NATURAL)
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public SortedSet<MonitoringExpressionAttribute> getAttributes() {
return attributes;
}
public void setAttributes(SortedSet<MonitoringExpressionAttribute> attributes) {
this.attributes = attributes;
}
ReportTrigger.Class
#Audited
#Table(name="ReportTrigger")
#Entity
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class ReportTrigger extends GeneratedIdXmlObject{
private String name;
private String description;
private TriggerableReport report;
private Frequency burstPeriodSize;
private String periodStartExpression;
private String periodEndExpression;
private Set<ReportTriggerParameterMapping> parameterMappings = new HashSet<ReportTriggerParameterMapping>();
private MonitoringExpression monitoringExpression;
#XmlIDREF
#ManyToOne
#Audited
#GraphProcessorOverride(process=false,recurse=false)
#NaturalId(mutable=true)
public TriggerableReport getReport() {
return report;
}
public void setReport(TriggerableReport report) {
this.report = report;
}
#Embedded
public Frequency getBurstPeriodSize() {
return burstPeriodSize;
}
public void setBurstPeriodSize(Frequency burstPeriodSize) {
this.burstPeriodSize = burstPeriodSize;
}
#Audited
#OneToMany(mappedBy="reportTrigger",orphanRemoval=true,cascade={CascadeType.ALL})
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public Set<ReportTriggerParameterMapping> getParameterMappings() {
return parameterMappings;
}
.
.
.
#XmlIDREF
#ManyToOne
#GraphProcessorOverride(process=false,recurse=false)
#NaturalId(mutable=true)
public MonitoringExpression getMonitoringExpression() {
return monitoringExpression;
}
public void setMonitoringExpression(MonitoringExpression monitoringExpression) {
this.monitoringExpression = monitoringExpression;
}
As far as I can tell, we're not doing anything out of the ordinary to the reportTrigger collection (and we obviously cannot be adding the same tigger twice to a set). Has anyone seen anything like this? Thanks
Hibernate 3.6.10
Java 8

I think can do some checking as below
- Check unique constraint violation error you got is on which field ? It can be on Primary key column but also can any unique columns.
- If the violation is with your primary key then while your collection is a set then it can be 2 different TriggerableReport but share the same key value.

Related

Hibernate - mapping an attribute OneToMany. (like OrderLine) Not association

I'm strugling with JPA. I tried several things but I can't figure out the right way to put the annotations.
What is want is like an Order/OrderLine relationship.
Thus:
Order( PK=orderId, fields=[...])
OrderLine (Pk1=orderId,Pk2=orderLineId, fields=[...])
Obviously, OrderLine.orderId refers to the 'Order' table.
What I functionally want to do is at least:
retrieve the Order with and without all orderlines. It should have a Set
retrieve an orderline by full PK, but without the associated Order.
retrieve a list of orderlines by orderId.
I only want these 2 tables and classes. nothing more nothing less.
I tried several things. Can anybody help me out with putting in the right annotations and members on these two classes?
Edit: what i've done so far.
Note that in this real example User=Order and UserRun=OrderLine. So, i am not interested in a seperate 'Run'-entity. Merely a UserRun as described by the Orderline.
#Entity
#Table(name = "user_runs")
public class UserRun {
#EmbeddedId
private UserRunKey id;
public UserRun(){};
public UserRun(String userName, String runUuid) {
this.id = new UserRunKey(userName, runUuid);
}
public String getUserName() {
return this.id.getUserName();
}
public String getRunUuid() {
return this.id.getRunUuid();
}
}
#Embeddable
class UserRunKey implements Serializable {
#Column(name = "username")
private String userName;
#Column(name = "run_uuid")
private String runUuid;
public UserRunKey(){};
public UserRunKey(String userName, String runUuid) {
this.runUuid = runUuid;
this.userName = userName;
}
public String getUserName() {
return userName;
}
public String getRunUuid() {
return runUuid;
}
}
This created a userruns/orderline table with the PK in the wrong way:
create table user_runs (run_uuid varchar(255) not null, username varchar(255) not null, primary key (run_uuid, username))
I want the primary key in reverse.
I want username as FK to User
I want a Set in my User-class.
When I do the following in my User-class:
#OneToMany
private Set<UserRun> userRuns;
It will create a
create table user_user_runs (user_username varchar(255) not null, user_runs_run_uuid varchar(255) not null, user_runs_username varchar(255) not null, primary key (user_username, user_runs_run_uuid, user_runs_username))
And that's something I definitely don't want! Once again, I don't want a Run-object (same as nobody's interested in a Line-class, from OrderLine)
I think I figured it out.
The UserRun/Orderline class:
#Entity
#Table(name = "user_runs")
public class UserRun {
#EmbeddedId
private UserRunKey id;
public UserRun(){};
public UserRun(String userName, String runUuid) {
this.id = new UserRunKey(userName, runUuid);
}
public String getUserName() {
return this.id.getUserName();
}
public String getRunUuid() {
return this.id.getRunUuid();
}
}
#Embeddable
class UserRunKey implements Serializable {
#Column(name = "username")
private String userName;
#Column(name = "run_uuid")
private String zrunUuid; //starts with a z, so the PK will be pk(username,run_uuid). Apparently, order in PK is determined from the variable names (alphabetic order)....
public UserRunKey(){};
public UserRunKey(String userName, String zrunUuid) {
this.zrunUuid = zrunUuid;
this.userName = userName;
}
public String getUserName() {
return userName;
}
public String getRunUuid() {
return zrunUuid;
}
}
In the userclass:
#OneToMany(mappedBy = "id.userName", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<UserRun> userRuns;
Unfortunately, there are 2 downsides:
I see that there are 2 queries executed instead of a Join on username. One to retrieve user, and 1 to retrieve the Set...
I needed to alter variablenames of the PK (compound/Embeddable). It seems there is no clean way to define the PK order. (Seriously?). Fortunately, the variable name is private, and not exposed by getter.
If anybody knows a cleaner way for these 2 issues. Let me know!
I think what you have to do is the following:
Because the primary key is compound key you need an ID class, as you already did:
#Embeddable
class OrderLinePK implements Serializable {
// you can use physical mapping annotations such as #Column here
#Column(name="...")
private Integer orderLineID;
// This is foreign key and the physical mapping should be done
// on the entity, and not here
private Integer orderID;
public OrderLinePK(){}
// getters + setters
// orverride equals() and hashCode() methods
}
Implement OrderLine entity
#Entity
public class OrderLine {
#EmbededId private OrderLinePK id;
#Mapsid("orderID")
#ManyToOne
#JoinColumn(name = "ORDER_ID", referencedColumn="ID")
private Order order;
// getters + setters ....
}
And the Order entity:
#Entity
public class Order {
#Id
private Integer id;
#OneToMany(fetch = FetchType.LAZY) // actually default by 1-to-n
private Coolection<OrderLine> orderLines;
// getters + setters ....
}

Many to Many relation with extra column query Hibernate

I did a #ManyToMany relationship in Hibernate with an extra column successfully, as follows.
Activity.class
#Entity
#Table(name = "Activity")
public class Activity {
#Id
#GeneratedValue
private int actId;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.activity",
cascade = { CascadeType.ALL, CascadeType.REMOVE })
private Set<ActivityRepairMap> activityRepairMaps = new HashSet<ActivityRepairMap>();
#NotEmpty
private String actTurno;
#NotEmpty
private String actTexto;
private String actFhc;
public Activity() {
}
// Getters and Setters
}
Repair.class
#Entity
#Table(name = "Repair2")
public class Repair {
#Id
#GeneratedValue
private int repId;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.repair")
private Set<ActivityRepairMap> activityRepairMaps = new HashSet<ActivityRepairMap>();
#NotEmpty(message=Constants.EMPTY_FIELD)
private String repNombre;
private Integer repCant;
public Repair() {
}
// Getters and Setters
}
ActivityRepairMap.class
#Entity
#Table(name="ActivityRepairMap")
#AssociationOverrides({
#AssociationOverride(name="pk.activity", joinColumns=#JoinColumn(name="actId")),
#AssociationOverride(name="pk.repair", joinColumns=#JoinColumn(name="repId"))
})
public class ActivityRepairMap {
private ActivityRepairId pk = new ActivityRepairId();
private Integer actRepCant;
#EmbeddedId
public ActivityRepairId getPk() {
return pk;
}
public void setPk(ActivityRepairId pk) {
this.pk = pk;
}
#Transient
public Activity getActivity() {
return getPk().getActivity();
}
public void setActivity(Activity activity) {
getPk().setActivity(activity);
}
#Transient
public Repair getRepair() {
return getPk().getRepair();
}
public void setRepair(Repair repair) {
getPk().setRepair(repair);
}
#Column(name="actRepCant")
public Integer getActRepCant() {
return actRepCant;
}
public void setActRepCant(Integer actRepCant) {
this.actRepCant = actRepCant;
}
public ActivityRepairMap (){
}
// hashCode and equals methods
}
ActivityRepairId
#Embeddable
public class ActivityRepairId implements Serializable{
private static final long serialVersionUID = -776429030880521951L;
private Activity activity;
private Repair repair;
#ManyToOne
public Activity getActivity() {
return activity;
}
public void setActivity(Activity activity) {
this.activity = activity;
}
#ManyToOne
public Repair getRepair() {
return repair;
}
public void setRepair(Repair repair) {
this.repair = repair;
}
// hashCode and equals method
}
My problem is that I can't query all the repairs used in a specific activity.
I've already checked in MySQL Workbench that the data stored in the DB is correct.
I would appreciate if anyone can explain me either using HQL or Criteria how can I achieve this.
Thanks a lot.
In SQL this should be:
SELECT
r.*
FROM
repair r
LEFT JOIN
activity_repair ar
ON
ar.repair_id = r.id
WHERE
ar.activity_id = ?
Now it's still possible that a single activity is connected with two repairs, and though you might get some repairs twice in the result list. You could simple use a SELECT DISTINCT r.* to work around this, or work with a subquery.
In JPQL the query should be bascially the same as the SQL from above.
SELECT
r
FROM
Repair r
WHERE
r.activityRepairMaps.pk.activity = ?
If you need a JOIN:
SELECT
r
FROM
Repair r
JOIN
ActivityRepairMap arm
WHERE
arm.pk.activity = ?
Maybe you need to use #MapsId within your ActivityRepairMaps class. (I haven't done JPQL for a while now)
As a far as I remember, you should NOT use Entities within your #EmbeddedId classes, but instead use the raw #Id type of the corresponding classes. Instead of Repair and Activity, you should use int and int.

Foreign key usage in Java with Hibernate

I am trying to develop a web application and I was wondering if there is a way to utilize a foreign key without writing a lot of code.
My Trainees.java
#Entity
public class Trainees {
#Id
#GeneratedValue
private int traineesID;
private int groupsID;
#ManyToOne
#JoinColumn(name = "status_trainee")
private String status_TraineeID;
private int customersID;
private String name;
private String surname;
private String phoneDetails;
private String email;
public Trainees(){
}
public Trainees(String name, String surname, String phoneDetails, String email, int id, int groupsID, String status_TraineeID, int customersID) {
super();
this.name = name;
this.surname = surname;
this.email = email;
this.phoneDetails = phoneDetails;
this.groupsID = groupsID;
this.status_TraineeID = status_TraineeID;
this.customersID = customersID;
}
//getters and setters
#Override
public boolean equals(Object object) {
if (object instanceof Trainees){
Trainees contact = (Trainees) object;
return contact.traineesID == traineesID;
}
return false;
}
#Override
public int hashCode() {
return traineesID;
}
}
Status_Trainee.java
#Entity
public class Status_Trainee {
#Id
#GeneratedValue
private int status_traineeID;
private String value;
public Status_Trainee(){
}
public Status_Trainee(String value, int id) {
super();
this.value = value;
}
//getters and setters
#Override
public boolean equals(Object object) {
if (object instanceof Status_Trainee){
Status_Trainee value = (Status_Trainee) object;
return value.status_traineeID == status_traineeID;
}
return false;
}
#Override
public int hashCode() {
return status_traineeID;
}
}
Error: Caused by: org.hibernate.AnnotationException: #OneToOne or #ManyToOne on uaiContacts.model.Trainees.status_TraineeID references an unknown entity: String
So my aim is that using the Trainees table and class, I could retrieve the value of Status_Trainee table using the foreign key. For example: if foreign keys ID is 2, then it would retrieve a string from status_trainee table where primary key would match the foreign key ID.
I am using models, controlers, hibernate and angularjs to display to the view, I don't really want to pass the table through all this, I thought using something like ManyToOne or JoinColumns would retrieve the value?
Thanks for all the help!
You should add a reference to StatusTrainee in Trainee and annotate that with OneToMany, ManyToOne or OneToOne. Depending on which kind of relationship you will need a list of StatusTrainee or just a StatusTrainee.
Tip: dont use underscores in class names.
First of all, it is not recommended to use "_" in a class name when using hibernate. Hibernate uses underscores when accessing foreignKeys. So Lets Say you rename your class to: TraineeStatus and the id change it to traineeStatusId..
Secondly, You can use the Hibernate annotations for what you need. but first you need to know how the relation is:
#OneToMany : One Trainee can have lots of statuses
#ManytoOne : Many trainees can have the same status
#OneToOne : one Trainee Can only have one status and the other way around.
Try this:
#Entity
public class Trainees {
#Id
#GeneratedValue
private int traineesID;
private int groupsID;
#OneToOne
private TraineeStatus status;
private int customersID;
private String name;
private String surname;
private String phoneDetails;
private String email;
...
You can change the #OneToOne for the one you need..
Remember that hibernate will try to map this in your Trainees mysql table as status_traineeStatusId, so if you have this column (as an integer) at your trainess table you are done :)..
That is it..
Hope it helps

Mapping a class that consists only of a composite PK without #IdClass or #EmbeddedId

I've got two tables A and B with simple PK's.
#Entity
public class A {
#Id
public int idA;
}
#Entity
public class B {
#Id
public int idB;
}
I want to map a new association class AB that simply stores the relations between A and B, with composite PK idA+idB. AB doesn't have any extra columns, just the relation between idA and idB.
Is it possible to map AB using a single class? I want to avoid having to create a new ABId class just to use it as #IdClass or #EmbeddedId in AB, and I don't want to map this with a #ManyToMany association on A or B.
Why do you want to map such a join table? Just use a ManyToMany association between A and B. This join table will then be handled automatically when you'll add/remove a B to/from the list of Bs contained in A.
See http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html_single/#d0e11402
If you really want to do that, then just map the two IDs with the #Id notation. The primary class will be the entity class itself (which must be serializable), as explained in the hibernate reference documentation. Note that this is Hibernate-specific.
It would be nice if #JBNizet 's suggestion worked. Unfortunately, there's an old bug which makes it impossible to adopt it in the version I'm using (3.3.1-GA)
I've finally sorted this out by defining an inner static ID class and using it as #IdClass:
#Entity
#Table(name="TABLE_AB")
#IdClass(value=ClassAB.ClassABId.class)
public class ClassAB implements Serializable {
private String idA;
private String idB;
#Id
public String getIdA(){ return idA; }
public void setIdA(String idA){ this.idA = idA; }
#Id
public String getIdB(){ return idB; }
public void setIdB(String idB){ this.idB = idB; }
static class ClassABId implements Serializable {
private String idA;
private String idB;
#Column(name="ID_A")
public String getIdA(){ return idA; }
public void setIdA(String idA){ this.idA = idA; }
#Column(name="ID_B")
public String getIdB(){ return idB; }
public void setIdB(String idB){ this.idB = idB; }
// HashCode(), equals()
}
}
This way I don't have to define a new public class, and I don't have to modify the mappings file to include the ID class.

Hibernate embedded map saving problem

I've been having trouble with saving embedded collections. I've come up with this crazy solution because I wanted my lazily loaded models to sort based on fields in resources.
I have three tables:
assets (id)
assets_resources (asset_id,resource_id,primary_image)
resources (id,logical_name)
I have defined the tables with hibernate annotations like so:
Assets.java:
#Entity
#Table(name = "assets")
public class Asset implements java.io.Serializable {
#OneToMany
#Cascade(CascadeType.ALL)
#JoinTable(
name="assets_resources"
, joinColumns=#JoinColumn(name="asset_id")
, inverseJoinColumns=#JoinColumn(name="resource_id")
)
#MapKeyColumn(name="asset_id")
#OrderBy("logical_name")
private Map<AssetResource, Resource> resources;
public Map<AssetResource, Resource > getResources() {return resources;}
public void setResources(Map<AssetResource, Resource> resources) {this.resources = resources;}
}
assetResource.java
#Embeddable
public class AssetResource {
private Boolean primary_image;
public Boolean getPrimaryImage() {return primary_image;}
public void setPrimaryImage(Boolean primary_image) {this.primary_image = primary_image;}
private Long id;
public Long getId() {return id;}
}
resource.java
#Entity
#Table(name="resources")
public class Resource implements java.io.Serializable {
...
public String logical_name;
public String getLogicalName() {return logical_name;}
public void setLogicalName(String logical_name) { this.logical_name = logical_name;}
}
I can iterate though the lazily loaded maps in my controllers but I can't seem to save/update embedded fields. specifically, I can't update the primary_image field.
This link demonstrates what I was trying to do.
Are you able to save the attribute value in database. Because i think you won't be able to save the value as u have not mapped it with some column in the database. Try removind Emmbedded and then add the table and column attributes..

Categories