jpa composite primary key and jointable with unique key - jpa

here are the sql tables:
option primary key is name + value
CREATE TABLE `option` (
id int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) NOT NULL,
`value` varchar(45) NOT NULL,
PRIMARY KEY (`name`, `value`),
UNIQUE KEY `id_UNIQUE` (`id`)
)
product primary key is incremental id
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
)
a product has several options (referenced by unique key "id")
CREATE TABLE product_option (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_product` int(11) NOT NULL,
`id_option` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `id_product` (`id_product`),
KEY `id_option` (`id_option`),
CONSTRAINT `FK_product_option` FOREIGN KEY (`id_product`) REFERENCES `product` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_option_product` FOREIGN KEY (`id_option`) REFERENCES `option` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
)
On java side, i've mapped "option" this way
#Embeddable
public class OptionId implements Serializable{
#Column(name="value")
private String value;
#Column(name="name")
private String name;
}
#Entity #Table(name="option")
public class Option {
#Column(name="id", unique=true)
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#EmbeddedId
private OptionId primaryKey;
public OptionId getPrimaryKey() {
return primaryKey;
}
public void setPrimaryKey(OptionId primaryKey) {
this.primaryKey = primaryKey;
}
}
and product this way
#Entity
#Table(name="product")
public class Product {
#Id #Column(name="id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Column(name="name")
private String name
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#JoinTable(
name="product_option",
joinColumns = #JoinColumn( name="id_product"),
inverseJoinColumns = #JoinColumn( name="id_option", referencedColumnName="id")
)
private Set<Option> options;
}
then at execution time i get this error
Caused by: java.lang.ArrayIndexOutOfBoundsException: 1
at org.hibernate.sql.SelectFragment.addColumns(SelectFragment.java:107)
at org.hibernate.persister.collection.BasicCollectionPersister.manyToManySelectFragment(BasicCollectionPersister.java:308)
at org.hibernate.persister.collection.BasicCollectionPersister.selectFragment(BasicCollectionPersister.java:294)
at org.hibernate.loader.JoinWalker.selectString(JoinWalker.java:1070)
at org.hibernate.loader.AbstractEntityJoinWalker.initStatementString(AbstractEntityJoinWalker.java:124)
at org.hibernate.loader.AbstractEntityJoinWalker.initStatementString(AbstractEntityJoinWalker.java:109)
at org.hibernate.loader.AbstractEntityJoinWalker.initAll(AbstractEntityJoinWalker.java:91)
at org.hibernate.loader.AbstractEntityJoinWalker.initAll(AbstractEntityJoinWalker.java:78)
at org.hibernate.loader.entity.CascadeEntityJoinWalker.<init>(CascadeEntityJoinWalker.java:52)
at org.hibernate.loader.entity.CascadeEntityLoader.<init>(CascadeEntityLoader.java:47)
at org.hibernate.persister.entity.AbstractEntityPersister.createLoaders(AbstractEntityPersister.java:3254)
at org.hibernate.persister.entity.AbstractEntityPersister.postInstantiate(AbstractEntityPersister.java:3191)
at org.hibernate.persister.entity.SingleTableEntityPersister.postInstantiate(SingleTableEntityPersister.java:728)
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:348)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1872)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:906)
at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:74)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:225)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:308)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
... 60 more
if i remove "referencedColumnName", of course i get the error
A Foreign key refering Option from Product has the wrong number of column. should be 2
but i can't figure out how to fix this

Based on your schemas, your entity annotations are slightly off. Where you specify 'product' and 'option' you should be using 'id_product' and 'id_option':
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#JoinTable(
name="product_option",
joinColumns = #JoinColumn( name="id_product"),
inverseJoinColumns = #JoinColumn( name="id_option", referencedColumnName="id")
)
private Set<Option> options;
As an aside, your life will be considerably easier, in the long run, if you switch the primary and unique keys on your option table.

Related

Spring Boot OneToOne referencedColumnName not applied

I have 2 tables:
CREATE TABLE user_profile (
id VARCHAR(36) PRIMARY KEY,
first_name VARCHAR(255) NOT NULL
);
CREATE TABLE user_phone (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36),
phone_number VARCHAR(255) NOT NULL,
code VARCHAR(10) NOT NULL,
validated_at DATETIME NULL
);
ALTER TABLE user_phone ADD CONSTRAINT user_phone_FK FOREIGN KEY (user_id) REFERENCES user_profile(id) ON DELETE CASCADE;
so child has ID reference to his parent, because if I will delete parent I want this child to be deleted also.
In User class I have:
#Entity
#Table(name = "user_profile")
public class UserProfile {
...
#OneToOne
#JoinColumn(name = "id", referencedColumnName = "user_id")
private UserPhone phoneNumber;
but then when I run it I see in JPA logs:
from
user_profile userprofil0_
where
userprofil0_.id=?
why it looks there by ID ? should be by user_id field. Or I missunderstood smth there..
thanks!
One day later and finally I found the solution.
Its pretty tricky in spring and not maybe so intuitive.
So - from user_phone table Ive removed additional ID column, and now user_id is there primary key, only 1 change in sql.
Then in spring:
in UserProfile:
#OneToOne(mappedBy = "userProfile", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private UserPhone userPhone;
and in UserPhone:
#Id
#Column(name = "user_id", nullable = false)
#Type(type = "uuid-char")
private UUID id;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
#JoinColumn(name = "user_id")
private UserProfile userProfile;
#PrimaryKeyJoinColumn annotation indicates that the primary key of the UserProfile is used as the foreign key value for the associated UserPhone.

Foreign Key violation on ManyToMany with inheritance

Im currently building the following scenario:
I have an Action which holds a list of Parameters. Those can be in other actions as well, so I have a ManyToMany relationship.
The Parameter is an abstract class, one implementation is a TextParameter.
So now I have the following code:
#Data
#Entity
public class Action {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(
name = "Action2ParameterMapping",
joinColumns = #JoinColumn(name = "actionId"),
inverseJoinColumns = #JoinColumn(name = "parameterId"))
private List<Parameter> parameters;
}
with Parameter as
#Data
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class ProductSample {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
And TextParameter:
#Data
#Entity
#PrimaryKeyJoinColumn(name = "parameterId")
public class TextParameter extends Parameter {
...
}
I now created the Tables as follows (I don't want to generate since we use Flyway migration):
CREATE TABLE Action
(
id BIGINT NOT NULL PRIMARY KEY IDENTITY
)
CREATE TABLE Parameter
(
id BIGINT NOT NULL PRIMARY KEY IDENTITY
)
CREATE TABLE TextParameter
(
parameterId BIGINT NOT NULL FOREIGN KEY REFERENCES Parameter (id)
)
-- Many-To-Many MappingTable
CREATE TABLE Action2ParameterMapping
(
actionId BIGINT NOT NULL FOREIGN KEY REFERENCES Action (id),
parameterId BIGINT NOT NULL FOREIGN KEY REFERENCES Parameter (id),
PRIMARY KEY (actionId, parameterId)
)
I use Quarkus and have the simple PanacheRepository
#ApplicationScoped
public class ActionRepository implements PanacheRepository<Action> {
}
So now, when I now create an Action-Object holding Parameter-Objects and persist it using actionRepository.persist(action), I get an SQLServerException The INSERT statement conflicted with the FOREIGN KEY constraint "FK__Action2Pa__actio__4242D080 and I don't understand why.
I understand that it tries to tell me, that it wanted to persist an entry in the MappingTable but the actionId did not belong to any Action, but how can that be?
I don't understand, why this won't work.
After having the problem for over 3 days, I've solved almost right after asking the question...
The problem was within the DB-Test-Suite.
The #AfterEachmethod tried to delete parameters, which violated the Contraint...

JPA: create constraint between primary key column in one table and a non-primary column in another?

I’m using JPA and trying to figure out how to create a unique constraint between a primary key column in one table and a non-primary key column in another. I have two tables:
Customer (
id character varying(32) NOT NULL,
customer_name character varying(50)
)
and
Account (
id character varying(32) NOT NULL,
account_name character varying(50)
)
There is a unidirectional, one-to-many relationship between Customer and Account with a constraint that Account.account_name is unique per Customer. The code looks like so:
#Entity
public class Customer {
#Id
#Column(length=32)
private String id;
#Column(unique=true, length=50)
private String customer_name;
#OneToMany
private List<Account> accounts;
...
}
and
#Entity
public class Account {
#Id
#Column(length=32)
private String id;
#Column(length=50)
private String account_name;
...
}
A join table is created by default:
CUSTOMER_ACCOUNTS (
customer_id character varying(32),
accounts_id character varying(32)
)
How do I create the unique constraint to ensure an Account.account_name is unique per Customer?
Add a JoinColumn annotation to the Customer class::
#Entity
public class Customer {
#Id
#Column(length=32)
private String id;
#Column(unique=true, length=50)
private String customer_name;
#OneToMany(mappedBy="customer")
#JoinColumn(name="CUSTOMER_ID", referencedColumnName="ID")
private List<Account> accounts;
...
}
Referenced column name points to the primary key of the Customer table, while name points to the foreign key field of the Account table.
You should also add a CUSTOMER_ID field and a composite unique constraint to the account entity:
#Entity
#Table(
uniqueConstraints=
#UniqueConstraint(columnNames={"CUSTOMER_ID", "ACCOUNT_NAME"})
)
public class Account {
#Id
#Column(length=32)
private String id;
#Column(length=50)
private String account_name;
#Column(name="CUSTOMER_ID")
private String customer_id;
...
}

Declare JPA entity for a tree element

I do have a DB table with unidirectional trees. Leafs of these trees can have several children/parents.
Cycles are restricted.
Here is my DB table definition:
CREATE MULTISET TABLE WORKFLOW_SEQ_REL ,NO FALLBACK ,
NO BEFORE JOURNAL,
NO AFTER JOURNAL,
CHECKSUM = DEFAULT,
DEFAULT MERGEBLOCKRATIO
(
WORKFLOW_SEQ_ID INTEGER NOT NULL,
REL_WORKFLOW_SEQ_ID INTEGER NOT NULL,
JOB_ID BIGINT)
PRIMARY INDEX ( WORKFLOW_SEQ_ID );
As you can see it doesn't have a primary key right now. But it would appear later :) Really PK is: JOB_ID+PARENT_ID+CHILD_ID.
The idea is:
REL_WORKFLOW_SEQ_ID = PARENT
WORKFLOW_SEQ_ID = CHILD
JOB_ID = TREE_IDENTIFICATOR (a determinant to separate different trees stored
in one table).
I'm trying to declare a JPA entity:
#Entity
#Table(name="WORKFLOW_SEQ_REL")
public class EtlWorkflowSeqNode {
#EmbeddedId
public EtlWorkflowSeqNodeId etlWorkflowSeqNodeId;
//#Column(name="JOB_ID")
//public Integer jobId;
#Embeddable
class EtlWorkflowSeqNodeId{
#Column(name="JOB_ID")
public Integer jobId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="REL_WORKFLOW_SEQ_ID")
//EtlWorkflowSeq.id = PK of EtlWorkflowSeq entity
public EtlWorkflowSeq parent;
#OneToMany(fetch=FetchType.EAGER /*, mappedBy="parent"*/)
#JoinColumn(name="WORKFLOW_SEQ_ID")
public Set<EtlWorkflowSeq> children;
}
}
And I gen an error:
Caused by: org.hibernate.AnnotationException: A Foreign key refering models.EtlWorkflowSeqNode from models.EtlWorkflowSeq has the wrong number of column.
should be 2
Here is EtlWorkflowSeq entity:
#Entity
#Table(name="WORKFLOW_SEQ")
public class EtlWorkflowSeq {
#Id
#Column(name="WORKFLOW_SEQ_ID")
public Integer id;
#OneToOne(fetch=FetchType.EAGER)
#JoinColumn(name="WORKFLOW_ID")
public EtlWorkflow etlWorkflow;
}
What do I do wrong?
UPD:
Here are table definitions:
--a bad design. PK should be: WORKFLOW_SEQ_ID + REL_WORKFLOW_SEQ_ID + JOB_ID
CREATE MULTISET TABLE WORKFLOW_SEQ_REL ,NO FALLBACK ,
NO BEFORE JOURNAL,
NO AFTER JOURNAL,
CHECKSUM = DEFAULT,
DEFAULT MERGEBLOCKRATIO
(
WORKFLOW_SEQ_ID INTEGER NOT NULL, --a ref to child
REL_WORKFLOW_SEQ_ID INTEGER NOT NULL, -- a ref to parent
START_TYPE_ID SMALLINT NOT NULL, -- a type of connection
DISABLE_START_TYPE_ID SMALLINT, -- other type of connection
JOB_ID BIGINT) -- a tree determinant,
PRIMARY INDEX ( WORKFLOW_SEQ_ID );
CREATE MULTISET TABLE WORKFLOW_SEQ ,NO FALLBACK ,
NO BEFORE JOURNAL,
NO AFTER JOURNAL,
CHECKSUM = DEFAULT,
DEFAULT MERGEBLOCKRATIO
(
WORKFLOW_SEQ_ID INTEGER NOT NULL, -- an id
WORKFLOW_ID BIGINT NOT NULL, -- a ref to original workflow, not interesting
IS_NAME VARCHAR(255) CHARACTER SET UNICODE NOT CASESPECIFIC, -- some name
INFO_SYSTEM_INST_CD VARCHAR(255) CHARACTER SET UNICODE NOT CASESPECIFIC, -- other name
DISABLE BYTEINT) -- so garbage
UNIQUE PRIMARY INDEX ( WORKFLOW_SEQ_ID ); -- it should also be a PK
The Idea is that several trees are stored in WORKFLOW_SEQ_REL
JOB_ID is a determinant for trees.
WORKFLOW_SEQ_ID, REL_WORKFLOW_SEQ_ID refer some cutomized template from REL_WORKFLOW_SEQ table.
I cannot help noticing that there is an inconsistency in your question.
You first state that:
Leafs of these trees can have several children/parents.
This in my believe makes the relationship between leafs many to many.
As I make of your question that EtlWorkflowSeq represent leafs, I think EtlWorkflowSeqNode represents the relationship between EtlWorkflowSeq objects?
However, the nodes point to one parent and many children.
You can use something like this to create something similar:
#Entity
#Table(name="WORKFLOW_SEQ")
public class EtlWorkflowSeq
{
#Id
#GeneratedValue
#Column(name="WORKFLOW_SEQ_ID")
public Integer id;
#ManyToOne
#JoinColumn(name="WORKFLOW_ID")
public EtlWorkflow etlWorkflow;
#ManyToMany
#JoinTable(name = "WORKFLOW_SEQ_REL")
private Set<EtlWorkflowSeq> children;
#ManyToMany(mappedBy = "children")
private Set<EtlWorkflowSeq> parents;
#ManyToOne
#JoinColumn(name = "JOB_ID", referencedColumnName = "id")
private Job job;
}
This would make EtlWorkflowSeqNode and EtlWorkflowSeqNodeId obsolete.
I also would like to state that when using an #Embeddable you should only use base types in them. Using other than base types is not possible/causes problems/is not standard (correct me if I'm wrong).
If you would like to use foreign keys in a composite primary key you can use this:
#Entity
public class Foo
{
#Id
private Long id;
}
#Entity
public class Bar
{
#EmbeddedId
private BarPK key;
#MapsId(value = "fooId")
#ManyToOne
#JoinColumns({
#JoinColumn(name = "foo_id", referencedColumnName = "id")
})
private Foo foo;
}
#Embeddable
public class BarPK
{
#Column(name = "id")
private Long id;
#Column(name = "foo_id")
private Long fooId;
}

one-to-one relationship using JPA

I have created two table using JPA. i need to give 1-1 relationship between these tables. Can any one tell me how to give relationship between these tables.
Simply add a column in the table "owning" the relation with a FK constraint. For example:
CREATE TABLE MYENTITYA (
ID BIGINT NOT NULL,
MYENTITYB_ID BIGINT
);
CREATE TABLE MYENTITYB (
ID BIGINT NOT NULL
);
ALTER TABLE MYENTITYA ADD CONSTRAINT SQL100326144838300 PRIMARY KEY (ID);
ALTER TABLE MYENTITYB ADD CONSTRAINT SQL100326144838430 PRIMARY KEY (ID);
ALTER TABLE MYENTITYA ADD CONSTRAINT FKB65AC952578E2EA3 FOREIGN KEY (MYENTITYB_ID)
REFERENCES MYENTITYB (ID);
That would be mapped like this:
#Entity
public class MyEntityA implements Serializable {
private Long id;
private MyEntityB myEntityB;
#Id
#GeneratedValue
public Long getId() {
return this.id;
}
#OneToOne(optional = true, cascade = CascadeType.ALL)
public MyEntityB getEntityB() {
return this.myEntityB;
}
//...
}
#Entity
public class MyEntityB implements Serializable {
private Long id;
#Id
#GeneratedValue
public Long getId() {
return id;
}
//...
}
If the relation between EntityA and EntityB is not optional, then add a NOT NULL constraint.