Prevent duplicate entry for unique constraint - java

I am trying to save tags which are related to an article in my MySQL database. The relation between those two columns is 1:N. Each item has an auto generated key. The name of a tag is unique.
If I insert a new article with an existing tag, I get a duplicate entry exception for the unique constraint (MySQLIntegrityConstraintViolationException). This are my two entities:
Article.java
#Entity
public class Article implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToMany(cascade = CascadeType.ALL)
#JoinTable
private Set<Tag> tags = new HashSet<Tag>();
/* getter and setter */
}
Tag.java
#Entity
public class Tag implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(unique = true)
private String name;
/* getter and setter */
}
Hibernate generate following tables: article, tag, article_tag. For the first article the records are correct.
I use following code to insert a new article (only for testing):
Article article = new Article();
Tag tag = new Tag();
/* set the values */
EntityManager em = EMF.getInstance().get();
em.getTransaction().begin();
em.merge(article);
em.getTransaction().commit();
How could I get JPA to use the existing tag for the article instead of create a new one. How do I set the relation between those so components correctly?

In general the relationship between articles and tags is a many-to-many relationship as an article may have many tags and each of these tags may be reused in many articles.
To indicate a many-to-many relationship the #ManyToMany annotation is required.
Also to make something clear, in the OP it is indicated a unidirectionaly one-to-many relationship as the #JoinTable annotation has been used on the many side. This is the reason a join table has been created. In addition as a consequence if a #ManyToOne annotation is used in Tag class the many-to-one will be another unidirectional relationship. Just be careful there as they will be handled as two independent unidirectional relationship with probable strange behaviour and any configuration will not affect both entities, since it is not a biderictional relationship.
Finally, if it is required to have a one-to-many unidirectional relationship but also reuse the tags, it is required to retrieve them based on their name, so that they have the correct record id and then set the to the Article instance. If you try to set a new instance of Tag which will have no record id but a name that already exists, then the jpa provider will try to insert the new tag and a unique constraint exception will be thrown, because of the duplicate tag name. Also will need to remove any unique constraint referred to tag_id in article_tag table.

Related

JPA: How do I set up an entity with several children and several parents of same entity type?

I'm trying to model a business entity, where said business can have several parent businesses and several child businesses. I'm not sure which relationships are suited, or even what mapping is appropriate. I'm familiar with SQL but new to ORM in Java.
My thinking is that a business can have many or none children, and a business can have many or none parents. Therefore I've tried setting both as OneToMany but also as OneToMany, both resulting in this error: Illegal use of mappedBy on both sides of the relationship.
The implementation:
#Entity
public class Business{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "parentOrgs")
private Collection<Business> chlidOrgs;
#OneToMany(mappedBy = "chlidOrgs")
private Collection<Business> parentOrgs;
// --- Getters and setters below ---
What am I not understanding here? Any and all help much appreciated.
Your current mapping is syntactically incorrect, because only one side of the relationship can be owning side. Owning side is the field defined by value of mappedBy attribute. More detailed explanation can be found from here.
Also removing mappedBy from the one side does not solve the problem, because counterpart of OneToMany must be ManyToOne. Removing it from the both sides leaves us with two unirectional associations, which is also not what is needed.
Because each Business can have multiple parents and it seems to be preferred to be able to navigate directly to the childrens as well, solution is to use bidirectional ManyToMany:
#Entity
public class Business {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(mappedBy = "parents")
private Collection<Business> childrens;
#ManyToMany
private Collection<Business> parents;
}
From database point of view this means following tables:
Business(id)
Business_Business(childrens_id, parents_id)
When necessary, name of the join table and columns can be controlled via JoinTable.

JPA join to inherited class

In jpa I have some entity (Content and Product for now but more can come) that should join to Comment entity.
Problem is I don't want to have extra field(and column in table) in Comment entity for each join entity (Products, Contents..) because these entities will increase in future.
I find one semi-solution is to use single table inheritance and create concrete Comment class like CommentContent, CommentProduct, and use discriminator column but joining to entities (Content and Product) still remain.
what do you suggest?
Edit:
sample relation for example between Comment and Content will be #MayToOne that many Comments belongs to one Content and so for Product..
Edit 2
in pure table relationship schema (without ORM like hibernate/jpa) I can and I do this kind of solution:
add to column in comment table 1-item_type and 2-item_id witch item_type specify other side table name (product, content in my question) and item_id specify foreign key to table that it name is at item_type column
How can I model this in Jpa/hibernate ORM?
You can model what you described with Hibernate like
class Content {
#OneToMany
#JoinColum(name = "item_id")
#Where("item_type = 'CONTENT'")
Set<Comment> comments;
}
class Product {
#OneToMany
#JoinColum(name = "item_id")
#Where("item_type = 'PRODUCT'")
Set<Comment> comments;
}
class Comment {
#Id
#GeneratedValue
Long id;
#Enumerated(STRING)
ItemType itemType;
Lont itemId;
}
enum ItemType {
CONTENT,
PRODUCT
}
You can try #ManyToOne for your upcoming entities. So here you don't need to add new columns in Comment entity(base table) Check this example : https://howtoprogramwithjava.com/hibernate-manytoone-unidirectional-tutorial/
What type or relation is this?
If it is #OneToOne you can put information about relation with Comment in Product and Content, and in Comment use option MappedBy to map them, you will skip the extra columns in Comment.
If it is #OneToMany you won't make any column in Comment but rather again put information in Product and Content and only use mappedBy on collection of this objects on the Comment site.
CheckForMoreInfo

Storing data using hibernate for inter related tables

I have two tables (say table A and table B). Table B has foreign key from table A primary key. I generated my java entities using netbeans IDE and i now have something like:
For table A:
#Entity
#Table(name = "WORKFLOW_TRANSACTION")
public class WorkflowTransaction implements {
#OneToMany(mappedBy = "wtId")
private Collection<WorkflowTask> workflowTaskCollection;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "APP_ID")
private BigDecimal appId;
.
.
.
For table B:
#Entity
#Table(name = "WORKFLOW_TASK")
public class WorkflowTask implements Serializable {
#JoinColumn(name = "WT_ID", referencedColumnName = "APP_ID")
#ManyToOne
private WorkflowTransaction wtId;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "TASK_ID")
private BigDecimal taskId;
#Column(name = "STEP_NUM")
private BigInteger stepNum;
.
.
.
Now my questions are:
What is the correct way to save data, should I create object for table B entity and set it in table A entity and then save table A?
I am generating entity beans using netbeans IDE feature. Are there any known disadvantages of it?, if yes, what?
You have bi-directional association, so you need to setTable B property in Table A entity class and also vice-versa. Since you have not declared cascading, you need to save the Table A entity first and then Table B entity.
Alternatively, if you save Table B and then Table A entity classes, the hibernate generates an extra SQL update command to maintain the relationship.
But if you want hibernate to save Table B entity when you save Table A entity then you need to add Cascade property:
#OneToMany(mappedBy = "wtId", cascade=CascadeType.ALL)
I don't think you will have any disadvantages if you use Netbeans for generating the entity classes, it save you time in writing the entities. But if you want to learn then writing entities without Netbeans is good.
To achieve this you can use two type of techniques XML mapping or Annotations
In both these techniques the common point is use
cascade="save-update"
What happens is that you set an attribute in one table getter and it will automatically insert into the many relation when inserting into one relation table

Hibernate Mapping One To Many Relationship:Parent's PK to Child's FK

I have a classic one to many relationship and while saving it with Hibernate, I am not able to pass parent's PK column value to Child's FK column.
Parent Class
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int holdingPK;
#OneToMany(mappedBy="holding",targetEntity=PolicyType.class,fetch=FetchType.LAZY, cascade = CascadeType.ALL)
#XmlElement(name = "Policy")
private Set<PolicyType> policy;
Child Class
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int policyPK;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="HoldingFK",nullable = false)
private HoldingType holding;
Here HoldingPKis a auto generated sequence column which represents primary key. Value gets generated when I insert a Holding row. So I want to pass HoldingPK value to child's HoldingFK column on the fly.
Test Code
HoldingType obj = new HoldingType();
obj.setCurrencyTypeCode("6");
obj.setHoldingKey("123");
Set<PolicyType> set = new TreeSet<PolicyType>();
PolicyType policy = new PolicyType();
policy.setJurisdiction("Haha");
set.add(policy);
obj.setPolicy(set);
session.save(obj);
transaction.commit();
So I am able to pass Child's other values to Child Table column, just Parent PK is not reaching to Child's FK column.
Here I am persisting XML document values to database. For this I am marshalling XML to Java Objects using JAXB then persisting objects using Hibernate. In this way I am reusing JAXB generated classes with Hibernate and these PK and FK elements do not exist on XML. These are specific to Database.
You simply forgot to initialise the owning side of the bidirectional association. You only initialized the inverse side (the one which has the mappedBy attribute). Hibernate only considers the owning side to know if an association exists or not (the side without the mappedBy attribute).
Add this to your code (before the holding is saved):
policy.setHolding(obj);
Side note: your code would be much more readable if you named the policy field (and accessors) policies. There are many of them, so it should have a plural form.

Limitation of JPA 1.0 using #IdClass with *nested* composite primary keys?

Given the following example (departments - projects):
A department has the following properties (composite primary key):
#Entity
#IdClass(DeptId.class)
public class Department
{
#Id
#Column(name="number")
private Integer number;
#Id
#Column(name="country")
private String country;
#Column(name="name")
private String name;
#OneToMany(mappedBy="dept")
private Collection<Project> projects;
...
}
Here the PK class:
public class DeptId implements Serializable
{
private Integer number;
private String country;
...
}
The relationship between projects and departments is many-to-one, that is a deptartment can have many projects. The Project class is itself using a composite key referencing Department's composite key. Important note: it's only about the implementation with #IdClass not #EmbeddedId.
Then the (problematic) JPA 1.0 #IdClass implementation would have to look something like that (redundant deptNum and deptCtry properties): -> it's just a unique name within a department
#Entity
#IdClass(ProjectId.class)
public class Project
{
#Id
#Column(name="dept_number")
private Integer deptNumber;
#Id
#Column(name="dept_country")
private String deptCountry;
#Id
#Column(name="name")
private String name;
#ManyToOne
#JoinColumns({
#JoinColumn(name="dept_number", referencedColumnName="number"),
#JoinColumn(name="dept_country", referencedColumnName="country")
})
private Department dept;
...
}
The ProjectId is:
public class ProjectId implements Serializable
{
private String name;
private DeptId dept;
...
}
The problem with this is that neither Hibernate nor EclipseLink know how to map the two redundant properties deptNum and deptCtry in Project to the dept property in DeptId (or the properies within it). -> MappingException etc.
My question is:
Is this a limitation of JPA 1.0, that tables with composite keys referencing other composite keys with #IdClass implementations generally WON'T work, because the JPA implementation simply can't know how to map these fields?
As a workaround, you'd have to use #EmbeddedId for these classes or use JPA 2.0 syntax to annotate the #XToX associations with #Id. I just want to make sure my view on this is right.
Thanks
Yes, this is a limitation of JPA 1.0, corrected in JPA 2.0. In the new JPA 2.0, you can put the ID annotation on your dept relationship and completely avoid having the redundent deptCountry and deptNumber attributes, with the key class using nesting. In JPA 1.0, only basic mappings can be marked as apart of the ID, requiring redundent mappings and some code to ensure that the values/relationships get put into the cache correctly when persisting. Because of the redundancy, as mentioned in other answers, one of the mappings for a field needs to be marked read-only via the insertable/updatable=false. Doing so though means that value is not merged into the cache - so changes (such as on insert, since you can't change an objects ID once it exists) will not be reflected unless the object is refreshed from the database. If you mark the JoinColumns as read-only, you will need to get the values from the referenced dept and put them into the correspoinding basic id attributes manually when you want to persist a Project. But, you can also mark the basic attributes as read-only. Eclipselink anyway will not have any problems and will correctly set the field values using the associated dept entity (as long as it is set before persist is called on the Project). Notice though that the basic attributes may or may not be populated when you read back the project in a different context- this will depend on if the entity is refreshed from the database or not. If they are read-only, they do not get merged into the shared cache since they, being read only, should not have changed. So they can be just ignored, or if they must be populated, the entity refreshed or the values set from the dept in an event.
This same model can be reused by using the JPA2.0 #MapsId, which will also maintain the basic mappings using the values from the relationship for you. Only benifit I see is that you don't need to access the relationship (potentially causing unneccessary joins or database access on lazy relationships) to get the foreign key/id field values.
As for the ZipArea EclipseLink exceptions, they are due to ZipAreaId having a ZipId zip attribute instead it being flattened out. JPA 1.0 requires the key class to have an attribute of the same type and name for each #ID attribute in the Entity.
The problem with this is that neither Hibernate nor EclipseLink know how to map the two redundant properties deptNum and deptCtry in Project to the dept property in DeptId
This is why you need to define the ManyToOne foreign key(s) as read-only with this kind of mapping. This is done by setting the JoinColumn attributes insertable and updatable to false.
So try the following:
#Entity
#IdClass(ProjectId.class)
public class Project
{
#Id
#Column(name="dept_number")
private Integer deptNumber;
#Id
#Column(name="dept_country")
private String deptCountry;
#Id
#Column(name="name")
private String name;
#ManyToOne
#JoinColumns({
#JoinColumn(name="dept_number", referencedColumnName="number", insertable=false, updatable=false),
#JoinColumn(name="dept_country", referencedColumnName="country", insertable=false, updatable=false)
})
private Department dept;
...
}
The problem with the posted code is, that JPA 1.0 really doesn't allow nesting of composite primary key classes. This ProjectId is invalid:
public class ProjectId implements Serializable
{
private String name;
private DeptId dept;
...
}
DeptId has to be flattened, like:
public class ProjectId implements Serializable
{
private Integer deptNumber;
private String deptCountry;
private String name;
...
}
I just got an EclipseLink version to go, but Hibernate has problems with that. I wonder how to tell Hibernate that JPA 1.0 is assumed.

Categories

Resources