hibernate one to one relationship mapping, save only inserts child - java

I have the following relationship with person and transaction (one to one in my case). I want to be able to save a Person with a Transaction attached resulting in two inserts. One in tbl_person and one in tbl_Transaction. But the following only generates one insert instead of two. The one insert is in tbl_Transaction:
`CREATE TABLE `tbl_person` (
`ID` char(36) NOT NULL,
`TransactionID` int(11) DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `TransactionID` (`TransactionID`),
CONSTRAINT `tbl_person_ibfk_1` FOREIGN KEY (`TransactionID`)
REFERENCES `tbl_Transaction` (`TransactionID`)
);
CREATE TABLE `tbl_transaction` (
`TransactionID` int(11) NOT NULL,
PRIMARY KEY (`TransactionID`)
);
#Table(name="tbl_person")
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
#ToString
#Data
public class Person {
#Id
#GeneratedValue(generator = "hibernate-uuid")
#GenericGenerator(name = "hibernate-uuid", strategy = "uuid2")
#Column(name="ID", nullable = false)
private String ID;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "transactionId")
private Transaction transaction;
}
#Table(name="tbl_transaction")
#Entity
#Data
public class Transaction {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer transactionId;
}
public class Service() {
public void saveTransaction(Transaction transaction) {
Person person = new Person();
person.setTransaction(transaction);
getSessionCurrent().save(person);
}
}
`
service.saveTransaction(transaction);
The service.saveTransaction returns with no exception but it only inserts the transaction but not the person.
Can any one tell me what I am doing wrong ??

you need to define a #OneToOne field in Transaction class
like specified in this question:
#OneToOne bidirectional mapping with #JoinColumn
and then add this line:
transcation.setPerson(person);

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.

Not null constraint violation when flushing unidirectional entities

I am playing a little bit with unidirectional and bidirectional mappings using #OneToMany and #ManyToOne annotations, but I cannot break the wall through for unidirectional one when persisting entities and flushing them into the database.
So, two tables delivery_company might have many delivery:
SQL (Oracle):
CREATE TABLE delivery (
delivery_id NUMBER(6) NOT NULL,
price NUMBER(5, 2) NOT NULL,
delivery_time DATE NOT NULL,
delivery_company_id NUMBER(2) NOT NULL
);
ALTER TABLE delivery ADD CONSTRAINT delivery_pk PRIMARY KEY ( delivery_id );
CREATE TABLE delivery_company (
delivery_company_id NUMBER(2) NOT NULL,
delivery_company_name VARCHAR2(15 CHAR) NOT NULL
);
ALTER TABLE delivery_company ADD CONSTRAINT delivery_company_pk PRIMARY KEY ( delivery_company_id );
ALTER TABLE delivery
ADD CONSTRAINT delivery_delivery_company_fk FOREIGN KEY ( delivery_company_id )
REFERENCES delivery_company ( delivery_company_id );
Unidirectional mapping:
#Entity
#Table(name = "Delivery")
class DeliveryUniDirectional
{
#Id
#SequenceGenerator(name = "delivery_id_sequence", sequenceName = "delivery_id_sequence", allocationSize = 1)
#GeneratedValue(generator = "delivery_id_sequence", strategy = GenerationType.SEQUENCE)
#Column(name = "delivery_id")
public Long deliveryId;
public BigDecimal price;
#Temporal(TemporalType.DATE)
public Date deliveryTime;
// setters, getters
}
#Entity
#Table(name = "delivery_company")
class DeliveryCompanyUniDirectional {
#Id
#Column(name = "delivery_company_id")
#SequenceGenerator(name = "delivery_company_id_sequence", sequenceName = "delivery_company_id_sequence", allocationSize = 1)
#GeneratedValue(generator = "delivery_company_id_sequence", strategy = GenerationType.SEQUENCE)
private Long deliveryCompanyId;
#Column(unique = true)
private String deliveryCompanyName;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "delivery_id", nullable = false, insertable = false, updatable = false)
private List<DeliveryUniDirectional> deliveries = new LinkedList<>();
// setters getters
}
When I run #DataJpaTest test:
#Test
public void insertDeliveryUniDirectional()
{
DeliveryCompanyUniDirectional deliveryCompany = new DeliveryCompanyUniDirectional();
deliveryCompany.setDeliveryCompanyName("aa");
DeliveryUniDirectional delivery = new DeliveryUniDirectional();
delivery.setPrice(BigDecimal.ONE);
delivery.setDeliveryTime(new Date());
deliveryCompany.getDeliveries().add(delivery);
entityManager.persist(deliveryCompany);
entityManager.flush();
}
I receive
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute batch ...
//
Caused by: java.sql.BatchUpdateException: ORA-01400: cannot insert NULL into ("TESTUSER"."DELIVERY"."DELIVERY_COMPANY_ID")
when entityManager.flush();.
I tried in DeliveryCompanyUniDirectional to use #JoinColumn without insertable and updatable, but in that case hibernate complains:
Error creating bean with name 'entityManagerFactory' defined in class path resource ...
// ...
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: wieczorek.jakub.shop.business.spring.model.domain.DeliveryUniDirectional column: delivery_id (should be mapped with insert="false" update="false")
Definitely there is a problem with NOT NULL constraint for the foreign key in delivery table. When I try it with bidirectional mapping, persisting and flushing work very good, but I would like to achieve the same using unidirectional.
Thanks for reading
Your #JoinColumn should be delivery_company_id since it's your foreign key
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="delivery_company_id", referencedColumnName="delivery_company_id", nullable = false)
private List<DeliveryUniDirectional> deliveries = new LinkedList<>();

Is it possible to use #JoinTable over a subclass that includes target + join fields?

My model (exemplified) is the following:
CREATE TABLE person (
id INT PRIMARY KEY,
name TEXT
...
);
CREATE TABLE team (
id INT PRIMARY KEY,
name TEXT
....
);
CREATE TABLE team_reference_persons (
team_id INT NOT NULL,
person_id INT NOT NULL,
uses_telephone BOOLEAN,
PRIMARY KEY (team_id, person_id),
FOREIGN KEY (team_id) REFERENCES team(id),
FOREIGN KEY (person_id) REFERENCES person(id)
);
And my JPA defintion:
#Entity
#Table(name = "team")
public class Team {
#Id
private Integer id;
#OneToMany
#JoinTable(name = "team_reference_persons", joinColumns = #JoinColumn(name = "team_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "person_id", referencedColumnName = "id"))
private List<Person> teamReferencePersons;
...
}
#Entity
#Table(name = "person")
public class Person {
#Id
private UUID id;
private String name;
...
}
So far, so good, when all you need is the person list on the team. But now I need to add the team_reference_persons.uses_telephone property from the join table in my person domain, So I am looking for a way to keep the persons logic, while I create a new subclass.
private class TeamIndividual extends Person {
boolean uses_telephone;
}
Then changing List<Person> on Team entity by List<TeamIndividual>. Is that possible someway? JPA should be indicated in such smart way that it adds the join table property to the final target entity (on both read and save).
No need to extend TeamIndividual to Person.
Annotate TeamIndividual with #Table(name = "team_reference_persons")
Define fields(teamId,personId,uses_telephone) inside TeamIndividual
Annotate fields teamId and PersonId with #ManyToOne and #JoinColumn
Add List to Team without annotation
Try this,It will work..!!

integrity constraint violation: unique constraint or index violation on Foreign key HSQL

I'm testing the underlying model of a HSQL database using Hibernate/Spring Boot and I've run into an issue I cannot find a solution to.
This is my simple test, I'm trying to create a shoebox entity and save it to the database with a User object set as the FK for Owner:
#TestConfiguration
static class ShoeboxServiceTestContextConfiguration {
#Bean
public ShoeboxService shoeboxService() {
return new ShoeboxService();
}
#Bean
public UserService userService() {
return new UserService();
}
}
#Autowired
UserService users;
#Autowired
ShoeboxService shoeboxes;
#Test
public void testSave()
{
System.out.println("save");
int userId = 1;
User user = new User(userId, "Foo", "hello#world.com");
user = users.save(user);
Shoebox sb = new Shoebox(user, "Name", "Context", "Comment", false);
UUID sbId = shoeboxes.save(sb).getId();
sb = shoeboxes.findOne(sbId);
assertNotNull(sb);
assertEquals(sb.getName(), "Name");
assertEquals(sb.getContext(), "Context");
assertEquals(sb.getComment(), "Comment");
assertEquals(sb.isShare(), false);
shoeboxes.deleteById(sbId);
users.deleteById(userId);
}
However when it gets it throws a
integrity constraint violation: unique constraint or index violation; SYS_PK_10126 table: USER
exception when it tries to save the Shoebox to the DB. It successfully persist the User, and it succeeds in persisting the Shoebox object when there is no Owner FK attached to it, but crashes when the FK is supplied.
Here is my User POJO:
#Entity
#Table(name="User")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class User implements Serializable
{
#Id
#Column(name = "ID")
private long ID;
#Column(name = "Name")
private String name;
#Column(name = "Email")
private String email;
#OneToOne(fetch = FetchType.LAZY)
private Shoebox currentlySelectedBox;
#OneToMany(fetch = FetchType.LAZY)
#JsonManagedReference(value="shoebox_owner")
private List<Shoebox> shoeboxes;
// Contructors, Getters/Setters etc.
}
And my Shoebox POJO:
#Entity
#Table(name="Shoebox")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Shoebox implements Serializable
{
#Id
#Column(name="ID")
UUID ID;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="OwnerID")
#JsonBackReference(value="shoebox_owner")
User owner;
#Column(name="Name")
String name;
#Column(name="Context")
String context;
#Column(name="Comment")
String comment;
#Column(name="Shared")
boolean share;
#Column(name="CreationDate")
LocalDateTime creationDate;
// Contructors, Getters/Setters etc.
}
Here is the HSQL creation script for the DB:
CREATE MEMORY TABLE PUBLIC.SHOEBOX(ID BINARY(255) NOT NULL PRIMARY KEY,COMMENT VARCHAR(255),CONTEXT VARCHAR(255),CREATIONDATE TIMESTAMP,NAME VARCHAR(255),SHARED BOOLEAN,OWNERID BIGINT)
CREATE MEMORY TABLE PUBLIC.USER(ID BIGINT NOT NULL PRIMARY KEY,EMAIL VARCHAR(255),NAME VARCHAR(255),CURRENTLYSELECTEDBOX_ID BINARY(255),CONSTRAINT FK3T924ODM2BIK5543K0E3UEGP FOREIGN KEY(CURRENTLYSELECTEDBOX_ID) REFERENCES PUBLIC.SHOEBOX(ID))
CREATE MEMORY TABLE PUBLIC.USER_SHOEBOX(USER_ID BIGINT NOT NULL,SHOEBOXES_ID BINARY(255) NOT NULL,CONSTRAINT FK5W8WMFC5E9RMEK7VC4N76MQVQ FOREIGN KEY(SHOEBOXES_ID) REFERENCES PUBLIC.SHOEBOX(ID),CONSTRAINT FKIR9SOKRCOQ33LCQTNR0LDXO93 FOREIGN KEY(USER_ID) REFERENCES PUBLIC.SHOEBOXUSER(ID),CONSTRAINT UK_508XA86IDIHP04FQD3D6GF8D7 UNIQUE(SHOEBOXES_ID))
ALTER TABLE PUBLIC.SHOEBOX ADD CONSTRAINT FK3J9RQBYW5VQ0IRF3FWYPG7LAB FOREIGN KEY(OWNERID) REFERENCES PUBLIC.USER(ID)
Why is the exception being triggered? Is there something wrong with my annotations and PK/FK relationships between the objects?
Many Thanks.
The issue is
#ManyToOne(cascade = CascadeType.ALL)
With CascadeType.ALL, any operations will extend to the other entities. So in this case the save method is cascading on the shoebox's user attempting to save it again. Since you are using a static id of 1, it is causing a key constraint.

HIbernate one-to-one annotation isn't generating foreign key GerericGenerator in dependent table

I am trying to create OneToOne relation between a Person and Auth table. The problem is when the DB table "Auth" is generated, I'm not seeing the foreign key in the AUTH table that should reference Person. The object is to have the Auth table use the same Primary Key of the Person Table.
#MappedSuperclass
public abstract class DomainBase {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Version
#Column(name="OPLOCK")
private Integer version;
}
#Entity
#Table(name = "person")
public class Person extends DomainBase {
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="auth_id")
private Auth auth;
}
#Entity
public class Auth {
#Id
#GeneratedValue(generator="foreign")
#GenericGenerator(name="foreign", strategy = "foreign", parameters={
#Parameter(name="property", value="person")
})
#Column(name="person_id")
private int personId;
---------------------------------
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Person person;
}
My Database scripts after hibernate DB generation.
CREATE TABLE auth
(
person_id integer NOT NULL,
activate boolean,
activationid character varying(255),
last_login_attempt_date timestamp without time zone,
last_login_attempt_timezone character varying(255),
last_login_date timestamp without time zone,
last_login_timezone character varying(255),
nonlocked boolean,
num_login_attempts integer,
CONSTRAINT auth_pkey PRIMARY KEY (person_id),
CONSTRAINT uk_d68auh3xsosyrjw3vmwseawvt UNIQUE (activationid)
)
WITH (
OIDS=FALSE
);
ALTER TABLE auth
OWNER TO postgres;
It seems that the problem is you declare twice the #OneToOne annotation between "person" table and "auth" table, without specify the relation between them. Take a look at the hibernate documentation, at the point 2.2.5.1, there is some examples about using one-to-one association.
For me, the best way is to set up the association in one table, the one that declare the foreing key column, and to use the mappedBy parameter in the other object. In your code, this will be :
#Entity
#Table(name = "person")
public class Person extends DomainBase {
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="auth_id")
private Auth auth;
}
#Entity
public class Auth {
#Id
#GeneratedValue(generator="foreign")
#GenericGenerator(name="foreign", strategy = "foreign", parameters={
#Parameter(name="property", value="person")
})
#Column(name="person_id")
private int personId;
#OneToOne(mappedBy = "auth")
private Person person;
....
}
This is the second example in the hibernate documentation, introduce just after the sentence "In the following example, the associated entities are linked through an explicit foreign key column". I tested this code, and the "auth_id" column appeared.

Categories

Resources