Composite key on table where primary key is also foreign key? - java

I have table item_image:
create table item_image
(
item_id bigint not null
constraint item_image_pk
primary key
constraint item_image_item_fk
references item,
location varchar(1000) not null,
path varchar(1000) not null,
file_name varchar(200) not null
);
And mapped it to entity:
#Entity
#Table(name = "item_image", schema = "public")
public class ItemImageEntity implements Serializable {
#Id
#OneToOne
#JoinColumn(name = "item_id")
private ItemEntity item;
private String location;
private String path;
private String fileName;
// getters and setters
}
My item entity has:
#OneToOne(mappedBy = "item")
private ItemImageEntity profileImage;
in it, the postgress table does not contain refference to this item_image, only vice versa.
When i try to run the app i get:
#EnableJpaRepositories declared on
JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration:
Invocation of init method failed; nested exception is
java.lang.IllegalArgumentException: This class [ItemImageEntity] does
not define an IdClass
Why do i need to define IdClass and How? Since i have only 1 primary key.
Thanks for help!

It's issue that you want to add primary key of entity without real primary key, hibernate can't identify itemEntity. (Do you have item_id field?)
About #IdClass - Hibernate can't identify class as field and think that you can use inner fields as primary key.

Related

JPA #OneToMany with composite primary keys

I need to map with JPA the following legacy DB table structure I cannot change.
It’s a one to many relationship between table ao_rda_acq (1) -> ao_rda_acq_righe (many) (purchase requisition -> purchase requisition rows)
Table (1)
create table ao_rda_acq
(
id_divisione varchar(4) not null,
esercizio smallint not null,
id_rda varchar(10) not null,
...
other fields
...
constraint pk_ao_rda_acq
primary key (id_divisione, esercizio, id_rda)
)
table many
create table ao_rda_acq_righe
(
id_divisione varchar(4) ,
esercizio smallint not null,
id_rda varchar(10) not null,
nr_riga integer not null,
...
other fields
...
constraint pk_ao_rda_righe
primary key (id_divisione, esercizio, id_rda, nr_riga),
constraint ao_rda_acq_righe_ao_rda_acq_id_divisione_esercizio_id_rda_fk
foreign key (id_divisione, esercizio, id_rda) references ao_rda_acq
)
The primary key of table ao_rda_acq side one of the relationship has 3 fields id_divisione, esercizio, id_rda. The primay key of the table side many of the relationship has the same 3 filed plus a 4th field nr_riga.
I tryed with this JPA approch using #IdClass annotation for composite primary keys
#Table(name="ao_rda_acq")
#Entity
#IdClass(RdaId.class)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Rda {
#Id
public String idDivisione;
#Id
public Integer esercizio;
#Id
public String idRda;
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
#JoinColumns({
#JoinColumn(name="id_divisione", referencedColumnName = "id_divisione"),
#JoinColumn(name="esercizio", referencedColumnName = "esercizio"),
#JoinColumn(name="id_rda", referencedColumnName = "id_rda")
})
#OrderBy("nrRiga")
public List<RdaRiga> righe = new ArrayList<>();
//Additional fields
}
where
public class RdaId implements Serializable {
String idDivisione;
Integer esercizio;
String idRda;
}
The entity for the rows is
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
#IdClass(RdaRigaId.class)
#Table(name = "ao_rda_acq_righe")
public class RdaRiga {
#Id
public String idDivisione;
#Id
public Integer esercizio;
#Id
public String idRda;
#Id
public Long nrRiga;
//More fields
}
where
public class RdaRigaId implements Serializable {
String idDivisione;
Integer esercizio;
String idRda;
Long nrRiga;
}
This code compiles but JPA at start-up complains with this message
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.DuplicateMappingException: Table [ao_rda_acq_righe] contains physical column name [id_divisione] referred to by multiple logical column names: [id_divisione], [idDivisione]
Is this approch the best one to map my unhappy DB structure? If so what did I miss?
When you use #Id without the #Column annotation, the name of the column is assumed to be the name of the annotated property.
Given that your DB column seems to be *id_divisione* you need to use also the annotation #Column(name = "id_divisione").
This also applies to the other properties annotated with #Id.

JPA: How can an #Embeddable object get a reference back to its owner, but the #Embeddable is in a lazy collection?

I know that if you want to reference back from #Embeddable to its parent you can set the parent "manually" in the setter and use #Access(AccessType.PROPERTY) for this embedded field as stated in this answer, but what if this embedded element is mapped in a collection, which is lazy loaded?
Actually not sure whether this is an issue, if not "manually" reference back from #embeddable to its parent, everything is fine.
#CollectionTable.JoinColumns() is used to set the foreign key columns of the collection table which reference the primary table of the entity, which means that once set this optional property, there is no necessary to "manually" reference back from #embeddable to its parent.
Use your case as example:
#Entity
public class Image {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
....
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "COMPUTERS", joinColumns = #JoinColumn(name = "ID_IMAGE"))
private List<Computer> computers;
}
#Embeddable
public class Computer {
#Column
private String ipAddress;
*****//This idImage field is not necessary
#Column(name = "ID_IMAGE", insertable = false, updatable = false)
private Long idImage;*****
}
Once comment out the field idImage and its #Column annotation, the generated SQL is:
create table IMAGES (
id bigint not null,
Name_Image varchar(255),
primary key (id)
)
create table COMPUTERS (
ID_IMAGE bigint not null,
ipAddress varchar(255)
)
alter table COMPUTERS
add constraint FKl1ucm93ttye8p8i9s5cgrurh
foreign key (ID_IMAGE)
references IMAGES
If "manually" declare the join column in the embeddable class, although the DDL are the same, the embeddable object will contain one extra field "imageId", which will cause the JDBC call parameter out of index when executing the INSERT operation.

Spring Data JPA insert with child along with parent entity by taking id from parent

I want to save Parent entity along with child entity into MySQL database by just calling save on parent. There is one to one mapping between Parent and Child entities. Parent ID is auto generated and we need to use same in the child as child's pk also.
I am using Spring Data JPA 2.0 (the JPA provider is Hibernate) and Spring MVC framework.
When are tried to insert entity I am getting following error.
root cause
org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value: com.serro.cbmapi.model.Child.parent; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value: com.serro.cbmapi.model.Child.parent org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:207)
Here is My DB Schema:
Parent Table:
CREATE TABLE `parent` (
`pid` int(11) NOT NULL AUTO_INCREMENT,
`parent_name` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`pid`) ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1;
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Child Table:
CREATE TABLE `child` (
`cid` int(11) NOT NULL,
`child_name` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`cid`),
CONSTRAINT `child_f1` FOREIGN KEY (`cid`) REFERENCES `parent` (`pid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Here is my Java Entity
Parent Entity:
#Entity(name="parent")
#NamedQuery(name="Parent.findAll", query="SELECT p FROM parent p")
public class Parent implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private int pid;
#Column(name="parent_name")
private String parentName;
//bi-directional one-to-one association to Child
#OneToOne(mappedBy="parent",cascade=CascadeType.ALL)
private Child child;
//getter, setters
}
Child Enity:
#Entity(name="child")
#NamedQuery(name="Child.findAll", query="SELECT c FROM child c")
public class Child implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private int cid;
#Column(name="child_name")
private String childName;
//bi-directional one-to-one association to Parent
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="cid")
#MapsId("cid")
private Parent parent;
//getter, setters
}
Here is my Main method
AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
ParentRepository parentResp = context.getBean(ParentRepository.class);
Parent parent = new Parent();
parent.setParentName("Parent1");
Child child = new Child();
child.setChildName("Child1");
parent.setChild(child);
parentResp.save(parent);
You should use cascade in both your objects. Try this:
#Entity(name="parent")
public class Parent implements Serializable {
//...
#OneToOne(mappedBy="parent",cascade=CascadeType.ALL)
private Child child;
//...
}
#Entity(name="child")
public class Child implements Serializable {
//...
#OneToOne(optional = false, fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
#JoinColumn(name="cid", referencedColumnName = "id")
private Parent parent;
//...
}
The exception
not-null property references a null or transient value: com.serro.cbmapi.model.Child.parent; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value: com.serro.cbmapi.model.Child.parent
says that the parent property in your Child object is null.
So to fix this you need to add this line of code:
child.setParent(parent);
Also as per the JPA Docs:
Designates a ManyToOne or OneToOne relationship attribute that
provides the mapping for an EmbeddedId primary key, an attribute
within an EmbeddedId primary key, or a simple primary key of the
parent entity. The value element specifies the attribute within a
composite key to which the relationship attribute corresponds. If
the entity's primary key is of the same Java type as the primary key
of the entity referenced by the relationship, the value attribute is
not specified.
your parent field in Child class should be declared without the value attribute for #MapsId :
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="cid")
#MapsId
private Parent parent;

Creating row in child table using jpa when we have a row in parent table

I have 2 tables:
CREATE TABLE `addr1` (
`id` int(11) DEFAULT NULL,
`addr2` varchar(20) DEFAULT NULL
) ENGINE=InnoDB;
CREATE TABLE `address` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`address` varchar(250) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
primary key of addr1 is set from address table. I created my entities using joined inheritance as
#Entity
#Table(name="addr1")
public class Addr1 extends Address {
#Column(name="addr2")
private String addr2;
// getters and setters follows
}
#Entity
#Table(name = "address")
#Inheritance(strategy=InheritanceType.JOINED)
public class Address implements Serializable {
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "address", nullable = false, length = 250, insertable = true)
private String address;
// getters and setters follows
}
Now I have a row in address table with id 2. I want to inset a row in addr1 table with same id (2). I had tried
Address addr = em.getReference(Address.class, new Integer(4));
Addr1 addr1 = new Addr1(addr);
addr1.setAddr2("dsdsd");
em.persist(addr1);
but I am getting an error
javax.persistence.PersistenceException:
org.hibernate.PersistentObjectException: detached entity passed to
persist: Addr1
Please help me.
Regards,
Praveen
It's impossible. Inheritance implies a is-a relationship. Your entity instances can thus be either a bare Address, or an Addr1. And a Java object can't change its type. You can't take an object of type Object, and ask the JVM to change its type and make it an object of type String instead. It's the same here.
If you need to do that, then you should use composition, not inheritance: an Address can have an Addr1. And you should thus have a OneToOne association beteween Address and Addr1, instead of an inheritance relationship.

Unique items in Hibernate collections

I have defined a collection in Hibernate like this:
...
public class Item {
...
#ElementCollection
List<Object> relatedObjects;
}
It creates a mapping table with colums item_id and object_id.
The problem is that object_id seems to be unique. In other words I can not have two different items being related to the same object. But that is what I want.
I would like the combination of item_id and object_id to be unique. How do I do that?
That's not what I'm experiencing. For the following entity:
#Entity
public class Person implements Serializable {
#Id
#GeneratedValue
private Integer id;
private String firstName;
private String lastName;
#Enumerated(EnumType.STRING)
private Gender gender;
#ElementCollection
private Set<String> nicknames = new HashSet<String>();
private String dept;
// getters, setters
}
The following tables get created:
create table Person (id integer generated by default as identity, dept varchar(255), firstName varchar(255), gender varchar(255), lastName varchar(255), primary key (id))
create table Person_nicknames (Person_id integer not null, nicknames varchar(255))
alter table Person_nicknames add constraint FK24F0D97B19ACB65E foreign key (Person_id) references Person
There is no unique constraint. But I can't say more without seeing your "Object" class (it's an embeddable class, right?).
PS: ElementCollection can't be a ManyToMany, this is more a OneToMany.

Categories

Resources