OneToMany relationship and composite primary keys cascading - java

I am using JPA (Hibernate) and trying to persist entire new entity with childs and composite keys, but when persisting childs i getting null in key. Table structure:
CREATE TABLE IDENTITY_DOCS
(
PERSON_ID BIGINT NOT NULL,
DOC_TYPE_ID BIGINT NOT NULL,
...
);
CREATE TABLE DOC_TYPES
(
DOC_TYPE_ID BIGINT PRIMARY KEY NOT NULL,
...
);
CREATE TABLE PERSONS
(
PERSON_ID BIGINT PRIMARY KEY NOT NULL,
...
);
Mappings:
#IdClass(...IdentityDocsPK.class)
#Table(name = "IDENTITY_DOCS")
#Entity
public class IdentityDocs {
#Id
#Column(name = "PERSON_ID", nullable = false)
private Long personId;
#Id
#Column(name = "DOC_TYPE_ID")
private Long docTypeId;
#ManyToOne
#MapsId("personId")
#JoinColumn(name = "PERSON_ID", referencedColumnName = "PERSON_ID")
private Employee employee;
...
}
public class IdentityDocsPK implements Serializable {
private Long personId;
private Long docTypeId;
...
}
#Table(name = "PERSONS")
#Entity
#Where(clause = "PERSON_TYPE_ID = 1")
public class Employee {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PERSON_ID")
#Id
private Long personId;
...
#OneToMany(mappedBy = "employee", fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
private List<IdentityDocs> identityDocs;
}
#Table(name = "DOC_TYPES")
#Entity
public class DocTypes {
#Id
#Column(name = "DOC_TYPE_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long docTypeId;
...
}
Test case:
Employee employee = new Employee();
employee.setFirstName("AAAA");
employee.setLastName("BBBB");
employee.setPersonNumber("1337");
IdentityDocs docs1 = new IdentityDocs();
docs1.setDocTypeId(1L); // already in database
docs1.setDocNumber("11111");
docs1.setDocSeries("11111");
docs1.setPlaceIssue("1111");
employee.setIdentityDocs(new ArrayList<IdentityDocs>());
employee.getIdentityDocs().add(docs1);
em.persist(employee);
Error code:
NULL not allowed for column "PERSON_ID"; SQL statement:
insert into IDENTITY_DOCS (DATE_EXPIRED, DATE_ISSUE, DOC_NUMBER, DOC_SERIES, PLACE_ISSUE, DOC_TYPE_ID, PERSON_ID) values (?, ?, ?, ?, ?, ?, ?)
I also tried without #MapsId (using insertable = false, updatable=false on relevant field), but with same result. Question #OneToMany and composite primary keys? is relevant, but i dont find answer there

Maybe should have an AUTO_INCREMENT clause on your person_id field in your DDL, or generate the key in some other way.

Related

how do i create entity class for a table having primary key as foreign key?

how do i create entity for area_item_table which have foreign key as primary key
the Item entity:
#Entity
#Table(name = Item.ITEM)
public class Item {
#Id
#Column(name = ITEM_ID)
private long itemId;
#OneToOne
#JoinTable(name = "area_item_table",
joinColumns = {#JoinColumn(name = ITEM_ID, referencedColumnName = ITEM_ID)},
inverseJoinColumns = {
#JoinColumn(name = AREA_ID, referencedColumnName = ID)})
private Area area;
[removed get set and unrelated fields from entity]
}
the Area entity:
#Entity
#Table(name = Area.AREA)
public class Area {
#Id
#Column(name = ID, nullable = false)
private Long id;
[removed get set and unrelated fields from entity]
the table area_item_table :
item_id
area_id
1
121
is there a way to create an Entity for this table without creating new primary key field

JPA mappedBy reference an unknown target entity

I am writing a simple inventory database that contains tables for products, orders and customers. The database definition can be found here:
CREATE TABLE public.customers
(
id integer NOT NULL DEFAULT nextval('customers_id_seq'::regclass),
title character varying(10) COLLATE pg_catalog."default" NOT NULL,
first_name character varying(50) COLLATE pg_catalog."default" NOT NULL,
middle_names character varying(50) COLLATE pg_catalog."default",
last_name character varying(50) COLLATE pg_catalog."default" NOT NULL,
email character varying(50) COLLATE pg_catalog."default" NOT NULL,
phone_number character varying(50) COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT customers_pkey PRIMARY KEY (id)
)
CREATE TABLE public.products
(
id integer NOT NULL DEFAULT nextval('products_id_seq'::regclass),
name character varying(100) COLLATE pg_catalog."default" NOT NULL,
sku integer NOT NULL,
inventory_on_hand integer NOT NULL,
reorder_threshold integer NOT NULL,
price numeric(5,2),
inventory_to_be_shipped integer NOT NULL,
CONSTRAINT products_pkey PRIMARY KEY (id)
)
CREATE TABLE public.order_items
(
id integer NOT NULL DEFAULT nextval('order_items_id_seq'::regclass),
product_id integer NOT NULL,
order_id integer NOT NULL,
CONSTRAINT order_items_pkey PRIMARY KEY (id),
CONSTRAINT order_items_order_id_fkey FOREIGN KEY (order_id)
REFERENCES public.orders (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION,
CONSTRAINT order_items_product_id_fkey FOREIGN KEY (product_id)
REFERENCES public.products (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
CREATE TABLE public.orders
(
id integer NOT NULL DEFAULT nextval('orders_id_seq'::regclass),
customer_id integer,
order_date date NOT NULL DEFAULT now(),
arrival_date date,
CONSTRAINT orders_pkey PRIMARY KEY (id),
CONSTRAINT orders_customer_id_fkey FOREIGN KEY (customer_id)
REFERENCES public.customers (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
I am trying to implement a Spring Security Resource server to perform CRUD operations on the database. I have implemented entity classes for each table in the database but when try to start the server I get a
org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: edu.finalyearproject.imsresourceserver.models.Order.customers in edu.finalyearproject.imsresourceserver.models.Customer.orders
My entity and repository classes can be found below:
Product.java:
#Entity
#Table(name = "products")
#Data
public class Product
{
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer sku;
private Float price;
private Integer inventory_on_hand;
private Integer reorder_threshold;
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
#JoinTable(
name = "order_items",
joinColumns = #JoinColumn(name = "product_id"),
inverseJoinColumns = #JoinColumn(name = "order_id")
)
private Set<Order> orders = new HashSet<>();
}
Customer.java
#Entity
#Table(name = "customers")
#Data
public class Customer
{
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String title;
private String first_name;
private String middle_names;
private String last_name;
private String email;
private String phone_number;
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Order> orders;
}
Order.java
#Entity
#Table(name = "orders")
#Data
public class Order
{
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name="customer_id", nullable=false)
private Customer customer;
private Date order_date;
private Date arrival_date;
#ManyToMany(mappedBy = "orders", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<Product> products = new HashSet<>();
}
I know the problem is related to the relationships between the entities, but I haven't been able to find a solution. Any help would be greatly appreciated.
Try to correct this:
#Entity
public class Customer
{
// ...
#OneToMany(mappedBy = "orders", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Order> orders;
}
to this:
#Entity
public class Customer
{
// ...
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Order> orders;
}
See additional explanation in the documentation.
And you should correct also your Product-Order #ManyToMany association. Only one side of this association should use #JoinTable other side should use mappedBy property of the #ManyToMany annotation. Something like this:
#Entity
public class Product
{
// ...
#ManyToMany(
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
fetch = FetchType.LAZY
)
#JoinTable(
name = "order_items",
joinColumns = #JoinColumn(name = "product_id"),
inverseJoinColumns = #JoinColumn(name = "order_id")
)
private Set<Order> orders = new HashSet<>();
}
#Entity
public class Order
{
// ...
#ManyToMany(
mappedBy = "orders",
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
fetch = FetchType.LAZY)
private Set<Product> products = new HashSet<>();
}
As it is stated in the documentation:
For #ManyToMany associations, the REMOVE entity state transition doesn’t make sense to be cascaded because it will propagate beyond the link table. Since the other side might be referenced by other entities on the parent-side, the automatic removal might end up in a ConstraintViolationException.
Also as this is explained in this section of the documentation:
If you forget to JOIN FETCH all EAGER associations, Hibernate is going to issue a secondary select for each and every one of those which, in turn, can lead to N+1 query issues.
For this reason, you should prefer LAZY associations.

manyToOne and oneToMany in hibernate&spring

I have 2 tables in database side(oracle)
create table GROUPS
(
ID NUMBER not null,
GROUP_NAME VARCHAR2(30)
)alter table GROUPS
add constraint ID primary key (ID)
and
create table ITEM_GROUP
(
ITEM_ID VARCHAR2(30) not null,
GROUP_ID NUMBER not null
)
alter table ITEM_GROUP
add constraint ITEM_GROUPD_ID primary key (ITEM_ID, GROUP_ID)
alter table ITEM_GROUP
add constraint ITEM_GROUP_FK01 foreign key (GROUP_ID)
references GROUPS (ID);
Than I have mapping classes in Java side. I want to make thing, when I am selecting group to take all his items too, and I want to save item with hibernate it is all .
#Entity
#Table(name = "GROUPS")
public class Group {
#Id
#Column(name = "ID", nullable = false)
#javax.persistence.SequenceGenerator(name = "groupIdGenerator", sequenceName = "GROUP_SEQ", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "groupIdGenerator")
private int id;
#Column(name = "GROUP_NAME")
private String groupName;
#JsonManagedReference
#OneToMany(fetch = FetchType.EAGER, mappedBy="group",cascade = CascadeType.ALL)
private List<GroupItems> groupItems = new ArrayList<>();
// setters and getters
}
#SuppressWarnings("serial")
#Embeddable
public class GroupItemPK implements Serializable {
#Column(name = "ITEM_ID")
private String merchantId;
#Column(name = "GROUP_ID")
private int id;
// getters , setters , constructors , equals hashcode methods
}
#Entity
#Table(name = "ITEM_GROUP")
public class GroupITEM {
#EmbeddedId
private GroupITEMtPK id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID")
#JsonBackReference
private Group group;
}
I am interested in did i make any mistakes in build relationship ? If I did what is my mistakes , because I can not do my select and save queries without exceptions.
I am trying to do in my Code
List<Group> list = sessionFactory.getCurrentSession().createQuery("from Group a").list();
and here is my Exception
org.hibernate.engine.jdbc.spi.SqlExceptionHelper could not extract ResultSet [n/a]
java.sql.SQLSyntaxErrorException: ORA-00904: "GROUPITE0_"."ID": invalid identifier

How to save complex objects using Hibernate?

I have three table:
CREATE TABLE catalog (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
type_id INT,
genre_id INT,
product_name VARCHAR(100),
FOREIGN KEY ( genre_id ) REFERENCES genres ( genre_id ),
FOREIGN KEY ( type_id ) REFERENCES types ( type_id )
);
CREATE TABLE genres (
genre_id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
genre_name VARCHAR(50)
);
CREATE TABLE types (
type_id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
type_name VARCHAR(50)
);
Also I have Java classes
#Entity
#Table(name = "catalog", catalog = "media_store_db")
public class Catalog implements Serializable {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "product_name", length = 100)
private String productName;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "genre_id", referencedColumnName = "genre_id")
private Genre genre;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "type_id", referencedColumnName = "type_id")
private Type type;
#Entity
#Table(name = "genres", catalog = "media_store_db")
public class Genre implements Serializable {
#Id
#Column(name = "genre_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "genre_name")
private String name;
#Entity
#Table(name = "types", catalog = "media_store_db")
public class Type implements Serializable {
#Id
#Column(name = "type_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "type_name")
private String name;
Is it possible to save (using save() method of Hibernate Session) Catalog object like this
Catalog catalog = new Catalog();
catalog.setProductName("Product");
catalog.setGenre(new Genre());
catalog.setType(new Type());
save(catalog);
without writing SQL? And what I need to do with Genre and Type? Should I set id of both instances?
UPD:
This code works just fine
Catalog catalog = new Catalog();
catalog.setProductName("12 Years a Slave");
catalog.setGenre(genreRepository.get(Long.valueOf(1)));
catalog.setType(typeRepository.get(Long.valueOf(1)));
Session session = cfg.getSession();
Transaction tx = session.beginTransaction();
session.save(catalog);
tx.commit();
session.close();
Sure, you can persist the generated Object in the database using persist(Object obj).
Well, you should test the function in a JUnit Test. In the business code it should do your DAO.
No, all the Ids are generated, you don't need to set the id. It is managed by Hibernate.
For your example the UnitTest should look like:
public class DataGenerationTest {
private EntityManager em;
#Before
public void init(){
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
em = emf.createEntityManager();
}
#Test
public void shouldAddSomeCatalogs(){
em.getTransaction().begin();
Catalog catalog = new Catalog();
catalog.setProductName("Proguct");
catalog.setGenre(new Genre());
catalog.setType(new Type());
em.persist(catalog);
em.getTransaction().commit();
em.close();
}
}
(Sure you have to rename the PersistenceUnit test from the EntityManagerFactory. It should match your named PersistenceUnit in the persistence.xml)
Other interesting lecture:
Hiberante Session Doc
Small example (GitHub)

EclipseLink #CascadeOnDelete only works sometimes?

I have two tables, "books" and "bookstores" that both have a #OneToMany reference to the "book_to_bookstore" table which hols the information how many books are in which bookstore.
If either a book or a bookstore is deleted, that information should vanish, too.
Therefore I use the standard JPA CascadeType.ALL and orphanRemoval=true. To make "proper" SQL schemas that also work when accessing it not with Java, I added the EclipseLink specific #CascadeOnDelete annotation, expecting the CASCADE ON DELETE to appear after both FOREIGN KEY statements.
Interestingly though, it only appears after one of them which I just can't explain!
I tried the DDL generation for MySQL and PostgreSQL but both are the same.
Why?
A compileable mini project is here:
svn+ssh://lathspell#svn.code.sf.net/p/lathspellsphp/code/java_test_eclipselink_cascade_bug
The important classes:
public class Bookstore implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "bookstoreId", orphanRemoval = true)
#CascadeOnDelete
private List<BookToBookstore> bookToBookstoreList;
...
public class Book implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "bookId", orphanRemoval = true)
#CascadeOnDelete
private List<BookToBookstore> bookToBookstoreList;
...
public class BookToBookstore implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#JoinColumn(name = "bookstore_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Bookstore bookstoreId;
#JoinColumn(name = "book_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Book bookId;
...
The generated SQL for MySQL, note the missing CASCADE ON DELETE in the last line:
CREATE TABLE books (id INTEGER AUTO_INCREMENT NOT NULL, PRIMARY KEY (id))
CREATE TABLE book_to_bookstore (id INTEGER AUTO_INCREMENT NOT NULL, book_id INTEGER, bookstore_id INTEGER, PRIMARY KEY (id))
CREATE TABLE bookstores (id INTEGER AUTO_INCREMENT NOT NULL, PRIMARY KEY (id))
ALTER TABLE book_to_bookstore ADD CONSTRAINT FK_book_to_bookstore_book_id FOREIGN KEY (book_id) REFERENCES books (id) ON DELETE CASCADE
ALTER TABLE book_to_bookstore ADD CONSTRAINT FK_book_to_bookstore_bookstore_id FOREIGN KEY (bookstore_id) REFERENCES bookstores (id)

Categories