I have an entity B which contains a map of entities <VC, P> in which some of the fields in P, e.g. A is not being linked with my join table and gives the error:
PSQLException: ERROR: column pricing1_.pricing_a does not exist
I am trying to make it such that when I persist my main entity, B, that all of the entities in my map will also be persisted as well (if possible) all in one go.
This error occurs both when I do
bRepo.save(b);
and
pRepo.saveAll(b.getPricing().values()); // by here the values at least exists in its own table (p)
bRepo.save(b);
here is what I have
Main entity B
#Setter
#Getter
#Entity
#Table(name = "b")
public class B implements Serializable {
#Id
#Column(nullable = false)
private String name;
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "b_p",
joinColumns = #JoinColumn(name = "b_name", referencedColumnName = "name"))
#MapKeyJoinColumns({
#MapKeyJoinColumn(name = "p_c"),
#MapKeyJoinColumn(name = "c_id")
})
private Map<VC, P> pricing = new LinkedHashMap<>();
...
}
The pricing maps key
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "v_c")
public class VC implements Serializable {
#EmbeddedId private VCId vcId;
}
and its (VC) composite key
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Embeddable
public class VCId implements Serializable {
#Enumerated(EnumType.STRING)
#Column(name = "p_c")
private PC pC;
#Column(name = "c_id")
private String cId;
}
the pricing map's value
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Embeddable
#Entity
#Table(name = "p")
public class P implements Serializable {
#EmbeddedId private PId pId;
}
and its (P) key
#Setter
#Getter
#NoArgsConstructor
#Embeddable
public class PId implements Serializable {
#Column(name = "a")
private BigDecimal a; // complains about this field
#Column(name = "d_a")
private BigDecimal dA; // and will probably complain about this one too
}
My tables
CREATE TABLE b
(
name VARCHAR(100) NOT NULL PRIMARY KEY,
...
);
CREATE TABLE v_c
(
p_c TEXT NOT NULL,
c_id VARCHAR(50) NOT NULL,
PRIMARY KEY (p_c, c_id)
);
CREATE TABLE p
(
a NUMERIC NOT NULL,
d_a NUMERIC NOT NULL DEFAULT 0.0,
PRIMARY KEY (a, d_a)
);
CREATE TABLE b_p
(
b_name VARCHAR(100) NOT NULL,
p_c TEXT NOT NULL,
c_id VARCHAR(50) NOT NULL,
a NUMERIC NOT NULL,
d_a NUMERIC NOT NULL DEFAULT 0.0,
PRIMARY KEY (b_name, p_c, c_id),
FOREIGN KEY (b_name) REFERENCES b (name) ON DELETE CASCADE,
FOREIGN KEY (p_c, c_id) REFERENCES v_c (p_c, c_id) ON DELETE CASCADE,
FOREIGN KEY (a, d_a) REFERENCES p (a, d_a) ON DELETE CASCADE
);
What am I doing wrong?
In the end I made the following changes and it worked:
Replaced the composite ids for p and v_c with auto increment ids AND added another new, auto increment field called pricing_p_id in my b_p table:
CREATE TABLE v_c
(
vc_id BIGSERIAL NOT NULL PRIMARY KEY,
p_c TEXT NOT NULL,
coin_id VARCHAR(50) NOT NULL
);
CREATE TABLE p
(
p_id BIGSERIAL NOT NULL PRIMARY KEY,
a NUMERIC NOT NULL,
d_a NUMERIC NOT NULL DEFAULT 0.0,
vc_id BIGSERIAL,
FOREIGN KEY (vc_id) REFERENCES v_c(vc_id) ON DELETE CASCADE
);
CREATE TABLE b_p
(
b_name VARCHAR(100) NOT NULL,
vc_id BIGSERIAL NOT NULL,
pricing_p_id BIGSERIAL NOT NULL,
PRIMARY KEY (b_name, vc_id, pricing_p_id),
FOREIGN KEY (b_name) REFERENCES b (name) ON DELETE CASCADE,
FOREIGN KEY (vc_id) REFERENCES v_c (vc_id) ON DELETE CASCADE,
FOREIGN KEY (pricing_p_id) REFERENCES p (p_id) ON DELETE CASCADE
);
and then updated the mapping for the pricing field to only look like this:
#OneToMany(cascade = CascadeType.ALL)
#MapKeyJoinColumn(name = "vc_id") // this
private Map<VC, P> pricing = new LinkedHashMap<>();
Related
I created hierarchy of two entities, which i implement using the JOINED inheritance strategy:
#Entity
#Table(name = "procedure")
#Inheritance(strategy = InheritanceType.JOINED)
public class MedicalProcedure{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Temporal(TemporalType.DATE)
#Column(name = "date")
private Date date;
#Temporal(TemporalType.TIME)
#Column(name = "time")
private Date time;
#ManyToOne(
fetch = FetchType.LAZY)
#JoinColumn(name = "doctor_id")
private Doctor doctor;
#ManyToOne(
fetch = FetchType.LAZY
)
#JoinColumn(name = "patient_id")
private Patient patient;
}
#Entity
#Table(name = "procedure_single")
public class DentalProcedure extends MedicalProcedure
{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "data")
private String data;
}
Problem occurs, when I try to get all of the DentalProcedures using getAll method from my generic DAO:
#Override
public List<T> getAll() {
Session session = currentSession();
CriteriaQuery<? extends T> cq = session.getCriteriaBuilder().createQuery(daoType);
cq.from(daoType);
List<? extends T> list = session.createQuery(cq).getResultList();
return (List<T>) list;
}
(*) daoType: class com.xyz.clinic.domain.DentalProcedure
Hibernate generates the following sql:
Hibernate:
select
dentalproc0_.id as id1_2_,
dentalproc0_1_.date as date2_2_,
dentalproc0_1_.doctor_id as doctor_i4_2_,
dentalproc0_1_.patient_id as patient_5_2_,
dentalproc0_1_.time as time3_2_, dentalproc0_.data as data1_3_
from procedure_single dentalproc0_ inner join procedure dentalproc0_1_ on dentalproc0_.id=dentalproc0_1_.id
But after that, the following exception is thrown:
java.sql.SQLSyntaxErrorException:
You have an error in your SQL syntax; check the manual that corresponds
to your MySQL server version for the right syntax to use near
'procedure dentalproc0_1_ on dentalproc0_.id=dentalproc0_1_.id' at line 1
Here are the sql statements i used to create tables:
CREATE TABLE `procedure` (
`id` int(11) NOT NULL,
`patient_id` int(11) DEFAULT NULL,
`doctor_id` int(11) DEFAULT NULL,
`date` date DEFAULT NULL,
`time` date DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_PATIENT_ID_idx` (`patient_id`),
KEY `FK_PATIENT_ID_idx1` (`doctor_id`),
CONSTRAINT `FK_DOCTOR_ID` FOREIGN KEY (`doctor_id`) REFERENCES `person` (`id`),
CONSTRAINT `FK_PATIENT_ID` FOREIGN KEY (`patient_id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `procedure_single` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`data` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `PK_SINGLE_PROCEDURE_ID` FOREIGN KEY (`id`) REFERENCES `procedure` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
I can't find the problem. Could anybody explain to me what is happening and how do i deal with this?
I have three tables
CREATE TABLE "ingredient" (
"id" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1) PRIMARY KEY,
"ingredient" VARCHAR(50) NOT NULL
);
CREATE TABLE "pizza" (
"id" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1) PRIMARY KEY,
"pizza" VARCHAR(50) NOT NULL
);
CREATE TABLE "pizza_structure" (
"pizza_id" INT NOT NULL,
"ingredient_id" INT NOT NULL,
"amount" INT NOT NULL
);
how to join them, to get Pizzas structure as a Map
#Entity
#Table(name = "ingredient")
public class Ingredient{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
public Ingredient() {
}
}
#Entity
#Table(name = "pizza")
public class Pizza {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany ????
private Map<Ingredient, Integer> pizzaStructure;
public Pizza() {
}
public Pizza(String name, Map<Long, Integer> pizzaStructure) {
this.name = name;
this.pizzaStructure = pizzaStructure;
}
}
do I need to create #Embeddable class PizzaStructure, if yes when how to use it?
now I'm getting an error
Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class:
how to join them, to get Pizzas structure as a Map
It seems to look like this:
#ElementCollection
#CollectionTable(name = "pizza_structure", joinColumns = {#JoinColumn(name = "pizza_id")})
#Column(name = "amount")
#MapKeyJoinColumn(name = "ingredient_id")
private Map<Ingredient, Integer> pizzaStructure;
do I need to create #Embeddable class PizzaStructure
No.
More info is here: Hibernate User Guide - Maps.
Note that table pizza_structure should have foreign keys to pizza and ingredient tables and also unique constrain of pizza_id and ingredient_id, like this (it's postgresql dialect):
create table pizza_structure
(
pizza_id ... constraint fk_structure_pizza references pizza,
ingredient_id ... constraint fk_structure_ingredient references ingredient,
amount ...,
constraint pizza_structure_pkey primary key (pizza_id, ingredient_id)
);
You have a manyToMany relationship between pizza and ingredient and an additional column in your relationship.
I found a similar question here: JPA 2.0 many-to-many with extra column
(I would comment, but i do not have enough reputation.)
Unable to find column with logical name: VERTICAL_MARKET_ID in org.hibernate.mapping.Table(bck_vertical_market) and its related supertables and secondary tables
at org.hibernate.cfg.Ejb3JoinColumn.checkReferencedColumnsType(Ejb3JoinColumn.java:582)
Can anyone help with this fail? None of existing posts help me. My classes which uses VerticalMarket looks like:
#Entity
#Table(name = "BCK_VERTICAL_MARKET")
public class VerticalMarketEntity implements Serializable {
private VerticalMarketID verticalMarketId;
private String name;
public VerticalMarketEntity() {
}
public VerticalMarketEntity(VerticalMarketID verticalMarketId) {
if (Assert.CHECK)
Assert.notNull(verticalMarketId, "Parameter for id must be set");
this.verticalMarketId = verticalMarketId;
}
#EmbeddedId
#AttributeOverride(name = "verticalMarketId", column = #Column(name = "VERTICAL_MARKET_ID", nullable = false, length = 100))
#Attribute(index = 0, primaryKey = true)
public VerticalMarketID getVerticalMarketId() {
return verticalMarketId;
}
#Attribute(index = 1, type = String100TD.class)
#Column(name = "NAME", length = 100)
#Basic
public String getName() {
return name;
}
}
#Entity
#Table(name = "BCK_CERTIFICATE")
public class CertificateEntity {
private VerticalMarketEntity verticalMarket;
#Relation(index = 2, target = VerticalMarketEntity.class)
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "VERTICAL_MARKET", referencedColumnName = "VERTICAL_MARKET_ID")
public VerticalMarketEntity getVerticalMarket() {
return verticalMarket;
}
#Entity
#Table(name = "BCK_OFFERED_SERVICE")
public class OfferedServiceEntity implements Serializable {
private VerticalMarketEntity verticalMarket;
#Relation(index = 2, target = VerticalMarketEntity.class)
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "VERTICAL_MARKET", referencedColumnName = "VERTICAL_MARKET_ID")
public VerticalMarketEntity getVerticalMarket() {
return verticalMarket;
}
}
CREATE TABLEBCK_VERTICAL_MARKET (
VERTICAL_MARKET_ID CHAR(36) NOT NULL,
NAME VARCHAR2(100 CHAR) NOT NULL
)
ALTER TABLE BCK_VERTICAL_MARKET ADD CONSTRAINT PK_VERTICAL_MARKET PRIMARY KEY (VERTICAL_MARKET_ID);
CREATE TABLEBCK_CERTIFICATE (
CERTIFICATE_ID CHAR(36) NOT NULL,
IS_OTHER NUMBER(1) NOT NULL,
VERTICAL_MARKET CHAR(36) NOT NULL,
NAME VARCHAR2(100 CHAR) NOT NULL
);
ALTER TABLE BCK_CERTIFICATE ADD CONSTRAINT PK_CERTIFICATE PRIMARY KEY (CERTIFICATE_ID);
ALTER TABLE BCK_CERTIFICATE ADD CONSTRAINT FK__C_VERTICAL_MARKET_ID
FOREIGN KEY (VERTICAL_MARKET) REFERENCES BCK_VERTICAL_MARKET (VERTICAL_MARKET_ID);
CREATE TABLE BCK_OFFERED_SERVICE (
OFFERED_SERVICE_ID CHAR(36) NOT NULL,
VERTICAL_MARKET CHAR(36) NOT NULL,
OFFERED_SERVICE_TYPE CHAR(36)
) ;
ALTER TABLE BCK_OFFERED_SERVICE ADD CONSTRAINT PK_OFFERED_SERVICES PRIMARY KEY (OFFERED_SERVICE_ID);
ALTER TABLE BCK_OFFERED_SERVICE ADD CONSTRAINT FK___O_S_VERTICAL_MARKET_ID
FOREIGN KEY (VERTICAL_MARKET) REFERENCES BCK_VERTICAL_MARKET (VERTICAL_MARKET_ID);
ALTER TABLE BCK_OFFERED_SERVICE ADD CONSTRAINT FK___O_S_T_ID
FOREIGN KEY (OFFERED_SERVICE_TYPE) REFERENCES BCK_OFFERED_SERVICE_TYPE (OFFERED_SERVICE_TYPE_ID);
This question has been asked in many forms here but none of the solutions seem to work for me. I'm trying to delete the parent entity and I want all of the child entities to also be deleted.
My entities:
#Entity
#Table(name = "item", catalog = "myshchema")
public class Item implements java.io.Serializable {
#JoinColumn(name = "item_id", insertable = false, updatable = false, nullable = false)
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<ItemCategory> categories;
/* Getters and Setters and other fields*/
}
Table for Item:
CREATE TABLE `item` (
`item_id` int(11) NOT NULL AUTO_INCREMENT,
`store_id` int(11) NOT NULL,
PRIMARY KEY (`item_id`),
UNIQUE KEY `item_id_UNIQUE` (`item_id`),
KEY `FK_ITEM_STORE_ID_idx` (`store_id`),
CONSTRAINT `FK_ITEM_STORE_ID` FOREIGN KEY (`store_id`) REFERENCES `store` (`store_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=84 DEFAULT CHARSET=utf8;
And my other entity
#Entity
#Table(name = "item_category", catalog = "myschema")
#IdClass(ItemCategoryIndex.class)
public class ItemCategory implements java.io.Serializable {
#Id
#Column(name = "category_id", unique = true, nullable = false, insertable = false, updatable = false)
private Integer categoryId;
#Id
private Store store;
#Id
private Item item;
#Id
private String categoryName;
/* Getters and Setters */
}
Table for ItemCategory:
CREATE TABLE `item_category` (
`category_id` int(11) NOT NULL AUTO_INCREMENT,
`store_id` int(11) NOT NULL,
`item_id` int(11) NOT NULL,
`category_name` varchar(45) NOT NULL,
PRIMARY KEY (`category_id`),
UNIQUE KEY `category_id_UNIQUE` (`category_id`),
UNIQUE KEY `IDX_UNIQUE_STORE_CATEGORY` (`store_id`,`item_id`,`category_name`) USING BTREE,
KEY `FK_CATEGORY_STORE_ID_idx` (`store_id`),
KEY `FK_ITEM_CATEGORY_ID_idx` (`item_id`),
CONSTRAINT `FK_CATEGORY_STORE_ID` FOREIGN KEY (`store_id`) REFERENCES `store` (`store_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_ITEM_CATEGORY_ID` FOREIGN KEY (`item_id`) REFERENCES `item` (`item_id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=162 DEFAULT CHARSET=utf8;
I try to delete the item like this:
Item item = entityManager.find(Item.class, idList.get(i));
entityManager.remove(item);
My logs show that Hibernate is trying to set the primary key for ItemCategory to null:
Hibernate: update myschema.item_category set item_id=null where item_id=?
ERROR o.h.e.jdbc.spi.SqlExceptionHelper.logExceptions 146 - Column 'item_id' cannot be null
I even tried looping through the child records and deleting them manually, but Hibernate still issues this update to null query. What am I doing wrong?
I have to break your problem down to two parts
First - let's talk about your database schema design.
According to your schema, item and item_category has a one-to-many relationship meaning an item can have/be-assigned-to different categories but different items cannot have/be-assigned-to the same category.
That is totally fine if it is indeed your business requirement, I mention it because it does not make sense to me and this circumstance rarely happens.
If what you want is that a category can have multiple items and vice versa, itemand item_category must be a many-to-many relationship. There should be a join table additionally.
Second - let's say the schema don't change
ItemCategory is the owner of the relationship because it has a foreign key item_id refering to item table. So the ItemCategoy should look roughly like this:
#Entity
#Table(name = "item_category")
public class ItemCategory {
#Id
private Integer categoryId;
private Store store;
#ManyToOne
#JoinColumn(name="item_id", /*cascade = ...*/)
private Item item;
private String categoryName;
/* Getters and Setters */
}
Your Item entity will be roughly like this:
#Entity
#Table(name = "item", catalog = "myshchema")
public class Item implements java.io.Serializable {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy="item")
private Set<ItemCategory> categories; //`mappedBy`used here because this entity is not the owner of the relationship according to what mentioned above
/* Getters and Setters and other fields*/
}
To remove all the child entities(ItemCategory) from Item , simply
em.remove(item);
The orphanRemoval is true, deleting the parent, the children will be deleted as well.
In Hibernate, you need to decide who is owning the relationship. If you have the parent side (ItemCategory) owning the relationship, you will find insertion/deletion of Item+ ItemCategory will involve update of item_id in ItemCategory table (which is what I observed from your exception). In most case it is not preferable. We usually let the children own the relationship. This is done by using mappedBy
(pseudo-code)
class Item {
//...
#OneToMany(mappedBy = "item", cascade=ALL, orphanRemoval=true)
private Set<ItemCategory> categories;
}
class ItemCategory {
//...
#ManyToOne
#JoinColumn(name="item_id")
Item item;
}
The trick here is mappedBy
I have 2 entities Customer and Address, the relationship is an address can belong to multiple customers.
Below is the customer class as you can see it has an reference to the address object, in the underlying customer table it is the id of the address. I have omitted getters and setters as well as some simple variables.
#Entity
#Table(name = "customer")
public class Customer implements Serializable {
#Id
#GeneratedValue
#Column(name = "customer_id")
private int customerId;
#ManyToOne
#JoinColumn(name = "store_id")
private Store store;
#ManyToOne
#JoinColumn(name = "address_id")
private Address address;
........
}
Below is the address class.
//Address Class
#Entity
#Table(name = "address")
public class Address implements Serializable {
#Id
#GeneratedValue
#Column(name = "address_id")
private int addressId;
#JoinColumn(name = "city_id")
#ManyToOne
private City city;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "address")
#JsonIgnore
List<Customer> customers;
......
}
I have tried persisting a new customer and a new address in a single call to persist like below. I have omitted some variables i set.
Customer cus = new Customer();
Address addr= new Address();
........
cus.setAddress(addr)
List<Customer> cusList= new ArrayList<>();
cusList.add(cus);
addr.setCustomers(cusList);
entityManager.persist(cus)
But i get an error saying the address_id in the customer table is null . I would have thought the JPA would have inserted the new address and then inserted the new customer with the address id column set to the new address id? Is my thinking here wrong? Or have i made a mistake in the mapping or how i am persisting the entities?
Another way i could do this is persist address first and then persist customer but would prefer to do it in a single persist if possible.
Below are the underlying tables.
//Customer Table
CREATE TABLE `customer` (
`customer_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`store_id` tinyint(3) unsigned NOT NULL,
`first_name` varchar(45) NOT NULL,
`last_name` varchar(45) NOT NULL,
`email` varchar(50) DEFAULT NULL,
`address_id` smallint(5) unsigned NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
`create_date` datetime NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`customer_id`),
KEY `idx_fk_store_id` (`store_id`),
KEY `idx_fk_address_id` (`address_id`),
KEY `idx_last_name` (`last_name`),
CONSTRAINT `fk_customer_address` FOREIGN KEY (`address_id`) REFERENCES `address` (`address_id`) ON UPDATE CASCADE,
CONSTRAINT `fk_customer_store` FOREIGN KEY (`store_id`) REFERENCES `store` (`store_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=608 DEFAULT CHARSET=utf8;
/Address Table
CREATE TABLE `address` (
`address_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`address` varchar(50) NOT NULL,
`address2` varchar(50) DEFAULT NULL,
`district` varchar(20) NOT NULL,
`city_id` smallint(5) unsigned NOT NULL,
`postal_code` varchar(10) DEFAULT NULL,
`phone` varchar(20) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`address_id`),
KEY `idx_fk_city_id` (`city_id`),
CONSTRAINT `fk_address_city` FOREIGN KEY (`city_id`) REFERENCES `city` (`city_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=619 DEFAULT CHARSET=utf8;
Thanks.
If you want to save a new Address with a Customer you need to add CascadeType.ALL
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "address_id")
private Address address;
And save all by this way (you don't need to add a customer to the address list because a customer refer to the address just by a foreign key address_id)
Customer cus = new Customer();
Address addr = new Address();
cus.setAddress(addr)
entityManager.persist(cus)
But this is not a very convenient way because of addresses are something like reference. So it is unusual to update address in reference by saving every customer.