Hibernate UnUniqueify a column in table(Solved)
I want a field set to be non-unique on itself but to be unique in combination with the other field, I got this table with two columns(composite primary keys); id (primary key) and object_proxy_id (primary key), this is exactly what I need but hibernate sets the object_proxy_id to be unique on itself so that value cant be duplicate in the table, and I need this column to accept duplicate values. Because every user has its own object proxy and these proxy's don't have to be necessarily unique.
This is what I want to achieve:
|-------------------------------|
| tbl_object_proxy |
| ------------------------------|
| Id (pk)| object_proxy_id (pk) |
|-------------------------------|
| 1 | 150 -- |
| 1 | 149 |= must be able to be DUPLICATE which is not the case right now.
| 2 | 150 -- |
| 2 | 151 |
|-------------------------------|
Current code:
#Entity
#Table(name = "tbl_user_settings", uniqueConstraints = {#UniqueConstraint(columnNames={"user_id"})})
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Settings implements Serializable
{
#Id
#SequenceGenerator(name="someSequence", sequenceName="SEQ_SOMENAME", allocationSize =1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="someSequence")
#Column(name="id")
private int setting_id;
#OneToOne
private User user;
#ManyToOne
private SomeObject someobject;
#ElementCollection
#CollectionTable(name="tbl_collection_name", joinColumns=
#JoinColumn(name="id"), uniqueConstraints = {#UniqueConstraint(columnNames={"id", "object_proxy_id"})})
#Column(name="SomeObject")
private Set<SomeObject> objectProxy;
/*...constructors and methods...*/
}
Results in:
-- Table schema
|-------------------|
| tbl_user_settings |
|-------------------|
| id |PK <<Unique>>
| user_id |FK reference tbl_user <<Unique>>
| object_id |FK reference tbl_object
|-------------------|
|------------------|
| tbl_object_proxy |
|------------------|
| id |PK reference tbl_user_settings
| object_proxy_id |PK reference tbl_object <<Unique>> BUT I DON'T WANT THIS TO BE UNIQUE ON ITSELF !!!!
|------------------|
EDIT:
The two primary key's in tbl_object_proxy are composite primary key's
I have tried Xeon's solution but it didn't work.
Short answer: replace the #ElementCollection by a #ManyToMany relation with a #JoinTable like this:
#ManyToMany
#JoinTable(
name="tbl_settings_objecteproxy_v2",
joinColumns = #JoinColumn(name = "id"),
inverseJoinColumns = #JoinColumn( name = "objectproxy_id"))
private Set<SomeObject> objectproxy;
See "2.2.5.3.2.1. Definition" in Hibernate Annotation Documentation
This results in a same side table but then without the unique constraint.
So now this is possible:
|-------------------------------|
| tbl_object_proxy |
| ------------------------------|
| Id (pk)| object_proxy_id (pk) |
|-------------------------------|
| 1 | 150 -- |
| 1 | 149 |= It works! The unique constraint is gone!
| 2 | 150 -- |
| 2 | 151 |
|-------------------------------|
Detailed answer and cause description:
Somehow the #ElementCollection created a collectiontable with a one to many relation of the referenced key (collection | inverse join) which adds a unique constraint to the key referencing the other side table to reflect the one to many relationship which I didn't want. So I dropped the #ElementCollection and replaced it by a #ManyToMany relation with a #JoinTable annotation. I have also tried to declare the #ManyToMany relation in the #ElementCollection but it kept adding the Unique constraint to the referenced key.
My Settings class does now look like this:
#Entity
#Table(name = "tbl_user_settings", uniqueConstraints = {#UniqueConstraint(columnNames={"user_id"})})
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Settings
{
#Id
#SequenceGenerator(name="someSequence", sequenceName="SEQ_SOMENAME", allocationSize =1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="someSequence")
#Column(name="id")
private int setting_id;
#OneToOne
private User user;
#ManyToOne
private SomeObject someobject;
#ManyToMany
#JoinTable(
name="tbl_settings_objecteproxy_v2",
joinColumns = #JoinColumn(name = "id"),
inverseJoinColumns = #JoinColumn( name = "objectproxy_id"))
private Set<SomeObject> objectProxy;
/*...the rest...*/
}
I had similar issue with articles and categories:
public class ArticleCategoriesEntity {
#EmbeddedId
public ArticleCategoriesIdPk getArticleCategoriesIdPk() {
return articleCategoriesIdPk;
}
public void setArticleCategoriesIdPk(ArticleCategoriesIdPk articleCategoriesIdPk) {
this.articleCategoriesIdPk = articleCategoriesIdPk;
}
...
#Embeddable
public class ArticleCategoriesIdPk implements Serializable {
public ArticleCategoriesIdPk() {
}
public ArticleCategoriesIdPk(Integer articleCategoryIdPk, Integer articleCategoryVersionFk) {
this.articleCategoryIdPk = articleCategoryIdPk;
this.articleCategoryVersionFk = articleCategoryVersionFk;
}
private Integer articleCategoryIdPk;
#Column(name = "article_category_id_pk")
public Integer getArticleCategoryIdPk() {
return articleCategoryIdPk;
}
public void setArticleCategoryIdPk(Integer articleCategoryIdPk) {
this.articleCategoryIdPk = articleCategoryIdPk;
}
private Integer articleCategoryVersionFk;
#Column(name = "article_cat_version_fk")
public Integer getArticleCategoryVersionFk() {
return articleCategoryVersionFk;
}
public void setArticleCategoryVersionFk(Integer articleCategoryVersionFk) {
this.articleCategoryVersionFk = articleCategoryVersionFk;
}
And you need to set Uniqueness of the two columns of embedded PK class
Related
I have a many to many relation in my class but the problem is that the table created by spring boot contains and id which is not a primary key.
My code looks like:
#JoinTable(name = "T_Commande_Produit",
joinColumns = #JoinColumn(name = "idCommande"),
inverseJoinColumns = #JoinColumn(name = "idProduit"))
private List<Produit> products;
And the table created looks like this
+-------------+--------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------+------+-----+---------+-------+
| id | bigint | NO | MUL | NULL | |
| id_commande | bigint | NO | MUL | NULL | |
| id_produit | bigint | NO | MUL | NULL | |
+-------------+--------+------+-----+---------+-------+
If you are creating ManyToMany relation then you don't need a third ID variable you can simply set the primary key as id_commande and id_produit combination and you don't need a third entity in this case.But if you still want to manually create a table that includes three fields that is the id, id_commande, and id_produit then you can create an Entity class That contains OneToMany Relation with others.
Code Example :
The third Entity is a table referencing both the Produit and Commande.
#Entity
class ThirdEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#ManyToOne
#JoinColumn(name = "id_commande")
private Commande commande;
#ManyToOne
#JoinColumn(name = "id_produit")
private Produit produit;
}
Produit Entity
#Entity
class ProduitEntity {
// constructors and other fields excluded for simplicity.
#OneToMany(mappedBy = "produit") // here produit is a object name of Produit in ThirdEntity
private List<ThirdEntity> list;
}
Commande Entity
#Entity
class CommandeEntity {
// constructors and other fields excluded for simplicity
#OneToMany(mappedBy = "commande") // here commande is a object name of Produit in ThirdEntity
private List<ThirdEntity> list;
}
I've got two tables in my database that look like this, with a foreign key from job_label.job_id to equivalent column in job_record. Additionally, the triple of job_id, label_key, and label in job_record has a unique constraint.
mysql> select * from job_record;
+--------+---------+
| job_id | state |
+--------+---------+
| 1 | success |
| 2 | running |
| 3 | errored |
| 4 | success |
+--------+---------+
mysql> select * from job_label
+--------+-----------+--------+
| job_id | label_key | label |
+--------+-----------+--------+
| 1 | name | job 1 |
| 1 | type | normal |
+--------+-----------+--------+
On the Java class side I have this class:
#Entity
#Table(name = "job_record")
public class JobRecord {
#Id
#Column(name = "job_id")
private String jobId;
#Enumerated(EnumType.STRING)
#Column(name = "state")
private JobState state;
}
I've tried to define a class for job_label that looks something like this:
public class JobLabelRecord {
#Enumerated(EnumType.STRING)
#Column(name = "label_key")
private JobLabelKey key;
#Column(name = "label")
private String label;
}
And then I want a field in JobRecord that provides me all labels for that Job as a List<JobLabelRecord>. However, nothing I've tried works.
I've tried declaring JobLabelRecord as Embeddable with the equivalent field in JobRecord as Embedded. I've tried using ManyToOne and OneToMany mappings, but that fails because there's no Id columns in JobLabelRecord (and nothing I do with those works correctly).
Am I supposed to be using an ElementCollection here instead? I've tried that as well, but without success.
Any ideas here? Thanks!
You can find a simple example for this by searching for "jpa elementcollection separate table", such as JPA Tutorial - JPA ElementCollection CollectionTable Override Example. Applying this example to your problem gives the following:
#Entity
#Table(name = "job_record")
public class JobRecord {
#Id
#Column(name = "job_id")
private Integer jobId;
#Enumerated(EnumType.STRING)
#Column(name = "state")
private JobState state;
#ElementCollection
#CollectionTable(name="job_label",
joinColumns=#JoinColumn(name="job_id"))
private List<JobLabelRecord> labels;
...
#Embeddable
public class JobLabelRecord {
#Enumerated(EnumType.STRING)
#Column(name = "label_key")
private JobLabelKey key;
#Column(name = "label")
private String label;
...
Note also the JobRecord id should probably be an Integer.
I had two POJO class named Task and Team.A team can have many tasks correspondingly but each task can be mapped only one team.
// Task POJO
#Entity
#Table(name = "Task")
public class Task extends Domain implements Serializable {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long taskId;
//Mapping To Team
#ManyToOne
#JoinColumn(name="teamId")
private Team teamId;
Team POJO
#Entity
#Table(name = "Team")
public class Team extends Domain implements Serializable {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long teamId;
#Column(nullable = false)
#OneToMany(mappedBy="teamId")
private List<Task> task = new ArrayList<Task>();
The issue is I couldn't Map/Save/Update and which is not affecting my Team Pojo.
//Task Table
+-----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+--------------+------+-----+---------+----------------+
| taskId | bigint(20) | NO | PRI | NULL | auto_increment |
| taskName | varchar(255) | NO | | NULL | |
| teamId | bigint(20) | YES | MUL | NULL | |
+-----------------+--------------+------+-----+---------+----------------+
The Team Table
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| teamId | bigint(20) | NO | PRI | NULL | auto_increment |
| teamName | varchar(255) | NO | | NULL | |
+----------+--------------+------+-----+---------+----------------+
So How do I map these two Pojo classes and affect when saving/deletion of data in the tables.
Thanks in Advance.
I discovered that you have defined #Column at the one to many association property (tasks), I assume it was a type error.
For bi-directional one to many association, updates one side of the relationship, the other side should also get updated, and be in sync. Below shown how the make both entity in sync:
#Entity
#Table(name = "Task")
public class Task extends Domain implements Serializable {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long taskId;
//Mapping To Team
#ManyToOne
#JoinColumn(name="teamId")
private Team team;
public void setTeam(Team team) {
this.team = team;
if (!team.getTasks().contains(this)) {
team.getTasks().add(this);
}
}
}
#Entity
#Table(name = "Team")
public class Team extends Domain implements Serializable {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long teamId;
#Column(nullable = false)
#OneToMany(mappedBy="team")
private List<Task> tasks = new ArrayList<Task>();
public void addTask(Task task) {
this.tasks.add(task);
if (task.getTeam() != this) {
task.setTeam(this);
}
}
}
#OneToMany(mappedBy="teamId", cascade=CascadeType.ALL), try to add this annotation above the member(attribute) task of the class Team
Complete your #manyToOne mapping in the Task entity by adding the referenced column in the Team entity; and also name the member as 'team', not 'teamId' i.e.:
#ManyToOne
#JoinColumn(name="teamId",
referencedColumnName="teamId")
private Team team;
To understand relationship mapping in JPA in general, see this: https://en.wikibooks.org/wiki/Java_Persistence/Relationships#Nested_Collections.2C_Maps_and_Matrices
I have a Spring Data JPA backend using Hibernate as the ORM implementation.
This is the model:
__________ _________________________
|Person | |MailConfig |
|________| |_______________________|
| id PK | | uid PK-FK(Person.uid) |
| uid | | ... |
| ... | | |
|________| |_______________________|
#Entity
#Table(name="Person")
public class PersonEntity{
#Id
private String id;
private String uid;
#OneToOne(mappedBy="id", fetch=FetchType.EAGER)
private MailConfigEntity mailConfigNotes;
...
}
#Entity
#Table(name="MailConfig")
public class MailConfigEntity implements Serializable{
#Id
#OneToOne
#JoinColumn(name="uid", table="Person", referencedColumnName="uid", insertable = false, updatable = false)
private PersonEntity id;
...
}
Person table is joined with MailConfig table through a field that is not Person's primary key. When I load an entity using personDAO.findOne(id) I can see the join in the query is performed against person.id instead of person.uid (on personent0_.id=mailconfig2_.uid). Any idea why this isn't working?
Query log:
select
personent0_.id as id8_2_,
personent0_.uid as uid8_2_,
mailconfig2_.uid as uid5_1_
from
Person personent0_
left outer join
mailconfig mailconfig2_
on personent0_.id=mailconfig2_.uid
where
personent0_.id=?
As per the documentation, check if this is a foreign key
There are three cases for one-to-one associations: either the
associated entities share the same primary keys values, a foreign key
is held by one of the entities (note that this FK column in the
database should be constrained unique to simulate one-to-one
multiplicity), or a association table is used to store the link
between the 2 entities (a unique constraint has to be defined on each
fk to ensure the one to one multiplicity).
Also the JoinColumn should be on the owner side of the relationship
I have legacy db.
+----+ +------------+ +------------+
|Site| |Content | |Program |
+----+ +------------+ +------------+
|id | |id:PK | |siteId:PK,FK|
|name| |siteId:FK | |code:PK |
+----+ |prog_code:FK| |name |
|prog_param | +------------+
+------------+
Table Content have one PK column. siteId columns is not part of PK.
Table Program have two PK column(siteId, code). It is composite PK. this table work as template. Only variable in program is parameter and it is on table Content's prog_param column.
I want to map this table to object like this.
+-----------+ +------------+ +-----------+ +---------------+
|Site | |Program | |ProgramId | |Content |
+-----------+ +------------+ +-----------+ +---------------+
|id:long | |id:ProgramId| |site:Site | |id:long |
|name:String| |name:String | |code:String| |site:Site |
+-----------+ |param:String| +-----------+ |program:Program|
+------------+ +---------------+
but i don't know how to mapping the 'Content' and 'Program' in this complex situation.
So, I have tried to make situation simple.
I have mapped prog_param to Content
+-----------+ +------------+ +-----------+ +-------------------+
|Site | |Program | |ProgramId | |Content |
+-----------+ +------------+ +-----------+ +-------------------+
|id:long | |id:ProgramId| |site:Site | |id:long |
|name:String| |name:String | |code:String| |site:Site |
+-----------+ +------------+ +-----------+ |program:Program |
|programParam:String|
+-------------------+
as code
#Entity
#Table(name = "SITE")
#Getter #Setter
public class Site {
#Id
#Column(name = "SITE")
protected String id;
#Column(name = "NAME")
protected String name;
}
#Entity
#Table(name = "PROGRAM")
#Getter #Setter
public class Program {
#EmbeddedId
protected ProgramId id;
#Column(name = "NAME")
protected String name;
}
#Embeddable
#Getter #Setter #EqualsAndHashCode
public class ProgramId implements Serializable{
#ManyToOne
#JoinColumn(name = "SITE")
protected Site site;
#Column(name = "code")
protected String code;
}
#Entity
#Table(name="CONTENT")
#Getter
#Setter
public class Content {
#Id
#Column(name = "ID")
protected Long id;
#ManyToOne
#JoinColumn(name = "SITEID",referencedColumnName="SITE")
protected Site site;
#OneToOne
#JoinColumns({
#JoinColumn(name="SITEID",referencedColumnName="SITE"),
#JoinColumn(name="PROG_CODE",referencedColumnName="CODE"),
})
protected Program program;
}
But this is not work. Hibernate throw 'Repeated column in mapping for entity' exception.
So I have research some solutions.
Finally I found this 'Repeated column in mapping for entity' Exception throwed when Hibernated can not determine to use which property.
Stackoverflows's solution is set 'insertable=false, updatable=false' to #JoinColumn, so let hibernate can determine to use which property.
I want use Content's property 'site' because it is used very frequently. And Site and Program property is used separately in my application.
So I try to set 'insertable=false, updatable=false' to Content's property 'program'
#OneToOne
#JoinColumns({
#JoinColumn(name="SITEID",referencedColumnName="SITE", insertable=false, updatable=false),
#JoinColumn(name="PROG_CODE",referencedColumnName="CODE"),
})
protected Program program;
This is not work too. 'Mixing updatable and non updatable columns in a property is not allowed' exception is throwed.
So I have set set 'insertable=false, updatable=false' to 'PROG_CODE', of course, Hibernate do not change PROG_CODE when I set different prog_code.
At this point, I have no idea.
Please let me know some idea to solve this situation.
I am having the same issue, two composite keys sharing an attribute fails with "Repeated column in mapping for entity". I think this might be a bug, ref: https://hibernate.atlassian.net/browse/HHH-4582. I am using Hibernate 3.6.4