JPA: implicity persist #ManyToOne entities - jpa

I have a similar table structure to the following:
Table_A(
a_id,
data,
primary key(a_id)
)
Table_B(
b_id,
a_id,
data,
primary key (b_id),
foreign key (a_id) references Table_A(a_id)
)
There is a one to many relationship between Table_A and Table_B. My question is, if I have an entity for each of these tables, where:
Entity Table_A is composed of a list of Table_B entities and
Entity Table_B does not necessarily need a reference to Table_A (just the fields b_id, a_id, data)
is it possible to annotate these entities in a way where I can persist a Table_A entity and the process will implicitly persist all Table_B entities with the value of the new generated Table_A primary key a_id.
Thanks in advance.
Here is what I have essentially . But I get the below exception. Looks like Table_B is being persisted prior to Table_A, therefore no a_id value exists.
#Entity
public class Table_A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "a_id")
private Integer id;
#OneToMany (cascade = CascadeType.PERSIST)
#JoinColumn(name="a_id")
private List<Table_B> bList;
private String data;
}
#Entity
public class Table_B {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "b_id")
private Integer id;
private String data;
}
ERROR: null value in column "a_id" violates not-null constraint
Detail: Failing row contains (null, Testing, 16)

Yes, with a cascade set to PERSIST on the OneToMany association.
Note that you shouldn't have aId in the B entity. Either make the association bidirectional, and have a field of type TableA, or leave it unidirectional and don't have any field in B mapped to a_id.
EDIT: AFAIR, you need to tell Hibernate that the column is not null, else it tries to insert everything first, and then populate the foreign keys:
#OneToMany (cascade = CascadeType.PERSIST)
#JoinColumn(name="a_id", nullable=false)
private List<Table_B> bList;

#OneToMany(mappedBy = "a_id", cascade = CascadeType.PERSIST)
instead of
#OneToMany(cascade = CascadeType.PERSIST) #JoinColumn(name="a_id")

Related

Spring JPA difficult delete

Can someone help with the following model, implemented, but unable to delete as desired
Author OneToMany with Book
Book ManyToMany with Genre
Genre ManyToMany with Book
I can create the db and add records. I believe my cascade designation is wrong as I cannot do the following
From Author, delete a book(yes odd,copyright issues:) have it deleted from Books BUT also have it removed from Genre
The problem is depending on what cascade I either get FK violation on Genre delete or FK violation on Book delete. Is it a deadlock? It is a trivial pattern and must be solveable. Thank you
Ultimately I think that cascade is best used to handle the case when all child relations are deleted but still I think it's a complicated annotation and not well understood by most. I think that if the Book entity owns the authors and genres relationships then the ORM has no choice but to delete the relations when a book is deleted.
When I setup a simple springboot project it seems to take case of all this.
#Entity
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Author {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
#ManyToMany(mappedBy = "authors")
#lombok.ToString.Exclude
private Set<Book> books;
}
#Entity
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Genre {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(mappedBy = "genres")
#lombok.ToString.Exclude
private Set<Book> books;
}
#Entity
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Book {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany
private Set<Author> authors;
#ManyToMany
private Set<Genre> genres;
}
And an simple example:
Author author = authorRepository.save(Author.builder().build());
Genre genre = genreRepository.save(Genre.builder().build());
Book book = bookRepository.save(Book.builder().authors(Collections.singleton(author)).genres(Collections.singleton(genre)).build());
System.out.println(book);
bookRepository.delete(bookRepository.getOne(book.getId()));
And the logs seem to indicate that authors and genres are cleaned up when a book is deleted.
Hibernate: insert into author (id) values (null)
Hibernate: insert into genre (id) values (null)
Hibernate: insert into book (id) values (null)
Hibernate: insert into book_authors (books_id, authors_id) values (?, ?)
Hibernate: insert into book_genres (books_id, genres_id) values (?, ?)
Book(id=1, authors=[Author(id=1)], genres=[Genre(id=1)])
Hibernate: select book0_.id as id1_1_0_ from book book0_ where book0_.id=?
Hibernate: delete from book_authors where books_id=?
Hibernate: delete from book_genres where books_id=?
Hibernate: delete from book where id=?
I think this is doing what your questioned is about.

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...

how to store one table primary as an another table foreign key using one-to-one relationship

I have two entities with one to one relationship, I want parent entity primary as a child entity foreign key along with child entity primary key
I have been trying with JPA #MapsId() but I could not succeed
my parent entity
#Id
#SequenceGenerator(initialValue=10000, name = "parent_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator="parent_seq")
#Column(name = "parent_id")
private long parentid;
#OneToOne(mappedBy="parentEntity", cascade = CascadeType.ALL)
private ChildEntity childEntity;
and in my child entity
#SequenceGenerator(initialValue=10000, name = "child_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator="child_seq")
#Column(name = "child_id")
private long childid;
#MapsId("parent_id")
#OneToOne
private ParentEntity parentEntity;
here I would like to create tables using JPA for that I have given
spring.jpa.hibernate.ddl-auto=create
this is working fine but I am expecting that parent_id column should be created inside my childEntity table but it is not creating and the parent_id should be inserted into child tables parent_id column.
When you are going to use #MapsId feature then your 'child' entity should have the 'simple' identifier without any generation. For example:
#Entity
#Table(name = "parents")
public class Parent {
#Id
#GeneratedValue(...)
private Long id;
// other stuff...
}
#Entity
#Table(name = "children")
public class Child {
#Id
private Long id;
#MapsId
#OneToOne(fetch = FetchType.LAZY)
private Parent parent;
// other stuff...
}
In this case, the children table will be like the following:
create table children (
parent_id bigint not null constraint children_pkey primary key,
-- other stuff...
constraint fk_children_parent_id foreign key (parent_id) references parents(id)
);
More info: The best way to map a #OneToOne relationship with JPA and Hibernate

JPA auto-generated key not reflected in foreign key of child table

Parent Table:
#Table(name="parent_table_t")
public class ParentTable implements Serializable {
#Id
#Column(name="contact_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer contactId;
---------
---------
#OneToOne (cascade = CascadeType.ALL, mappedBy = "parentTable")
private ChildTable childTable;
}
Child Table:
#Table(name="child_table_t")
public class ChildTable implements Serializable {
#Id
#Column(name="child_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer childId;
#Column(name="contact_id")
private Integer contactId;
#JoinColumn(name="contact_id", referencedColumnName = "contact_id", insertable=false, updatable=false)
#OneToOne(cascade = CascadeType.ALL)
private ParentTable parentTable;
}
My requirement is when contact_id is generated in Parent_table_t, it should be copied into contact_id of child_table_t when saved.
When I am calling saveAndFlush / save on Parent Table Entity, it is:
Generating the auto-increment for Parent->contact_id.
But Child_table_t -> contact_id is always null.
Can someone please help in this.
I am using in-memorty hsqldb with spring-boot and JPA.
You marked the relationship #JoinColumn with insertable=false, updatable=false, likely because you have an integer mapping for the column as well. Unfortunately, these settings prevent JPA from setting it with values from the relationship, which instead is forced to set the column with the value in the contactId attribute.
Put the insertable=false, updatable=false on the #Column instead.

Why doesn't Eclipselink generate the ON DELETE CASCADE clause in a unidirectional #ManyToOne relationship?

These are two related entities in Eclipselink JPA:
#Entity
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
#ManyToOne(cascade={CascadeType.ALL})
private Department department;
}
and this is the generated DDL:
CREATE TABLE PERSON (ID BIGINT IDENTITY NOT NULL, FIRSTNAME VARCHAR, LASTNAME VARCHAR, DEPARTMENT_ID BIGINT, PRIMARY KEY (ID))
CREATE TABLE DEPARTMENT (ID BIGINT IDENTITY NOT NULL, NAME VARCHAR, PRIMARY KEY (ID))
ALTER TABLE PERSON ADD CONSTRAINT FK_PERSON_DEPARTMENT_ID FOREIGN KEY (DEPARTMENT_ID) REFERENCES DEPARTMENT (ID)
The environment is:
- eclipselink 2.5.2
- mysql-connector-java 5.1.6
I would expect to have at least a ON DELETE CASCADE clause on the foreign key definition.
What is the cascade option intended for, in the #ManyToOne relationship?
Do I really have to delete the children records manually before deleting the parent record?
The CascadeType you mention in your example is one value of those: ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH. This is ORM, but not SQL DDL related.
I guess You are searching for a SQL DDL foreign key constraint definition ...on delete cascade. To geht this SQL DDL generated, You need an #CascadeOnDelete annotation, as shown in this example:
...
#OneToMany(mappedBy="abc", orphanRemoval=true, cascade={CascadeType.ALL})
#CascadeOnDelete
private List<MobilPhoneNumer> mobilePhonesNumbers;
...