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
Related
I have two JPA entities : Ring and Principal
#Entity
class Ring {
#Id
int rId;
String description;
}
#Entity
class Principal {
#Id
int pId;
String description;
String type;
// ?? required annotations
Ring ring;
}
Though the Principal to Ring relationship is one-to-one, I have to maintain a separate table principals_rings.
DB schema looks like:
Ring
-----------------
rId | description
Principal
-------------
pId | description | type
principals_rings
----------------
pId | rId
I don't want to create any foreign keys to table principals_rings.
How can I join Principal table with principal_rings with hibernate annotations ? I want to have the Ring object inside the Principal class.
I tried with #OneToOne mapping and #JoinTable() but both of them create foreign key on
principals_rings.
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 have a problem using JPA.
I have to tables:
-----------------
| TableA |
|---------------|
| ID: INT |
| ... |
| ESTATUS1: INT |
| ESTATUS2: INT |
-----------------
-----------------
| EstatusTags |
|---------------|
| COD: VARCHAR |---> COD and VALUE are a concatenated PK
| VALUE: INT |
| DESC: VARCHAR |
-----------------
EstatusTags is a table to store sets of pairs [VALUE, DESC], given a COD.
Before I use JPA, I used to query this kind of data in something like this:
SELECT ID, ESTATUS1, ESTATUS2, E1.DESC DESC1, E2.DESC DESC2
FROM TABLEA A
INNER JOIN ESTATUSTAGS E1 ON E1.COD = "a_estatus1"
AND E1.VALUE = A.ESTATUS1
INNER JOIN ESTATUSTAGS E2 ON E2.COD = "a_estatus2"
AND E2.VALUE = A.ESTATUS2
I'm trying to use JPA to model this using two entity classes:
#Entity
#Table(name = "EstatusTags")
public class EstatusTags implements Serializable {
#EmbeddedId
private ValueTagPK id;
#Column(name="VVA_DESC")
private String desc;
#Column(name="VVA_ORDEN")
private Integer orden;
}
#Entity
#Table(name = "TableA")
public class A implements Serializable {
#Column(name="ID")
private String desc;
#OneToOne(???)
private EstatusTag estatus1;
#OneToOne(???)
private EstatusTag estatus2;
}
I have strong doubts in how to model the relations. Can it be done with annotations? There is necesary the JPQL use to fit this structure?
I hope somebody could help me with this.
Thanks a lot.
The problem is that your entity model does not match the table structure.
In your entity model you have a one to one relation ship between A and EstatusTag whereas in your table model you have a relationship of one A and multiple Estatustags (for one value there may exist multiple Etatustags entries)
You overcome the problem that Table A does not have a cod column by adding something like a virtual cod column E1.COD = "a_estatus1" to your SQL Query.
What you can do is you map the value column of to two properties of EstatusTag one time to the composite pk and the other time to a single property in the following way . The simple value is made accessible via property access but marked as not updatable not insertable also the setter does not really work and is made private.
Remark: I don't know if that works with all JPA implementations - Tested with hibernate 4.3.8.
#Entity
#Table(name = "EstatusTags" )
#Access(AccessType.FIELD)
public class EstatusTag implements Serializable{
private #EmbeddedId ValueTagPK id;
#Column(name="VVA_DESC")
private String desc;
#Column(name="VVA_ORDEN")
private Integer orden;
#Column(name="value", updatable=false, insertable=false)
#Access(AccessType.PROPERTY)
public int getValue() {
return id.value;
}
private void setValue(int value) {
// only because otherwise hibernate complains about a missing setter.
}
}
#Entity
#Table(name = "TableA")
public class A implements Serializable{
#Id
#Column(name="ID")
#GeneratedValue(strategy=GenerationType.TABLE)
private int id;
#OneToOne()
#JoinColumn(name="estatus1",referencedColumnName="value")
public EstatusTag estatus1;
#OneToOne()
#JoinColumn(name="estatus2",referencedColumnName="value")
public EstatusTag estatus2;
}
I have the following tables (most essential columns shown only, A & B are not the real names btw):
table A {
...
}
table B {
...
}
table METADATA {
KEY
VALUE
REF_A
REF_B
}
METADATA holds additional key/value meta data for both table A & B. The key/value is needed as we have to handle dynamic data for which we cannot up front create columns for in A and B.
The entities are setup as (JPA using hibernate as provider):
interface Entity {
...
getId()
...
}
class A implements Entity {
...
#OneToMany(cascade = {ALL}, mappedBy = "a", orphanRemoval = true, fetch = LAZY)
private List<MetaData> metaData;
...
#Override
public List<MetaData> getMetaData() {
return metaData;
}
...
}
class B implements Entity {
...
#OneToMany(cascade = {ALL}, mappedBy = "b", orphanRemoval = true, fetch = LAZY)
private List<MetaData> metaData;
...
#Override
public List<MetaData> getMetaData() {
return metaData;
}
...
}
class MetaData implements Entity {
...
#ManyToOne
#JoinColumn(name = "REF_A", nullable = true)
private A a;
#ManyToOne
#JoinColumn(name = "REF_B", nullable = true)
private B b;
...
}
This setup works fine. However we have run into issues on some databases (for instance DB2) with a unique index we create (to ensure a meta key is only used once for a given row in A or B):
CREATE UNIQUE INDEX METADATA_UNIQUE_KEY ON METADATA (METAKEY, REF_A, REF_B)
as creating the index requires requires that all columns are non-null. This is not the case for use with the above design as the domain logic will either be that the meta data is set on A or B, hence one of these will always be null.
Possible solutions of course are to split the METADATA into two tables, one for A and one for B. However I would prefer to keep one table and instead just have one "REF" column which would either be an A or B as well as a TYPE column to say whether it's a meta data for an A or B. The TYPE would be needed as we have separate sequences for id for each table and a A and B could get the same technical id and hence get mixed up data otherwise.
My question is - is there any way to set this up with JPA?
For one-table based inheritance there is a #DiscriminatorValue which can be used to distinguish the specific stored sub-class, can this be used here as well? I am looking for something like:
table A {
...
}
table B {
...
}
table METADATA {
KEY
VALUE
REF
TYPE
}
#DiscriminatorValue("A")
class A implements Entity {
...
#OneToMany(cascade = {ALL}, mappedBy = "entity", orphanRemoval = true, fetch = LAZY)
private List<MetaData> metaData;
...
#Override
public List<MetaData> getMetaData() {
return metaData;
}
...
}
#DiscriminatorValue("B")
class B implements Entity {
...
#OneToMany(cascade = {ALL}, mappedBy = "entity", orphanRemoval = true, fetch = LAZY)
private List<MetaData> metaData;
...
#Override
public List<MetaData> getMetaData() {
return metaData;
}
...
}
class MetaData implements Entity {
...
#ManyToOne
#JoinColumn(name = "REF", nullable = true)
private Entity entity;
#DiscriminatorColumn(name="TYPE", discriminatorType=STRING, length=20)
private String type;
...
}
so basically when a meta data is inserted for A this SQL would be used:
INSERT INTO METADATA (KEY, VALUE, REF, TYPE) VALUES ("metaKey", "metaValue", 1, "A")
Any suggestion are welcomed.
Rgs,
-Martin
I'm not sure why you need to create a key (metakey) in the metadata table, given thet the rows are already tied either to Table A or Table B.
However I think the problem is in considering the MetaData table an entity, given that its only purpose is to save some extra information of an existing entity, it means that you cannot have a row in MetaData without a row in TableA or TableB.
Instead of using relationship mapping one option is to use element collections to directly have the Map of key/value pairs in the corresponding entities:
#Entity
#Table(name="TableA")
public class TableA
{
#Id
#GeneratedValue(strategy= GenerationType.TABLE)
private int id;
#ElementCollection
#CollectionTable(name="MetaData", joinColumns={#JoinColumn(name="TableA_id")})
#MapKeyColumn(name="metaKey")
#Column(name="metaValue")
private Map<String, String> metadata;
}
#Entity
#Table(name="TableB")
public class TableB
{
#Id
#GeneratedValue(strategy= GenerationType.TABLE)
private int id;
#ElementCollection
#CollectionTable(name="MetaData", joinColumns={#JoinColumn(name="TableB_id")})
#MapKeyColumn(name="metaKey")
#Column(name="metaValue")
private Map<String, String> metadata;
}
Note that there is no java class for a "MetaData" table or entity, the table is automatically mapped from the #ElementCollection and #CollectionTable annotations.
The above mappings correspond to the following MetaData table:
+-----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| TableA_id | int(11) | YES | MUL | NULL | |
| metaValue | varchar(255) | YES | | NULL | |
| metaKey | varchar(255) | YES | | NULL | |
| TableB_id | int(11) | YES | MUL | NULL | |
+-----------+--------------+------+-----+---------+-------+
If you prefer to keep a separate java class for MetaData to keep using a List instead of a Map, it can also be done with #ElementCollection, you just need to annotate the MetaData class with #Embeddable instead of #Entity. In that way it doesn't need an Id column like a regular entity.
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