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.
Related
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<>();
I'm new in Java world, and JPA. I have a problem with OneToMany relationships.
So I have two entities: UserEntity and ManagerEntity.
The Manager is a User that was promoted, so here we have OneToOne relationship where manager_uuid in managers table reference uuid in the users table. This relationship works fine.
Now each Manager has many clients => UserEntity and it is mapped by managers table.
The problem is that it keeps looking for client_uuid in the users table instead of managers and I don't know why...
#Entity
#Table(name = "users")
public class UserEntity {
#Id
#GeneratedValue(strategy = AUTO)
private UUID uuid;
#OneToOne
#JoinColumn(
name = "manager_uuid",
referencedColumnName = "uuid")
private ManagerEntity managerReference;
#ManyToOne
#JoinColumn(name = "client_uuid",
referencedColumnName = "uuid")
private ManagerEntity manager;
}
#Entity
#Table(name = "managers")
public class ManagerEntity {
#Id
#GeneratedValue(strategy = AUTO)
private UUID uuid;
#OneToOne(mappedBy = "managerReference")
private UserEntity actingМanager;
#OneToMany(mappedBy = "manager")
private List<UserEntity> clients = new ArrayList<>();
}
Migration:
CREATE TABLE IF NOT EXISTS managers
(
uuid uuid DEFAULT gen_random_uuid() PRIMARY KEY,
manager_uuid uuid NOT NULL,
client_uuid uuid NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
FOREIGN KEY (manager_uuid) REFERENCES users (uuid),
FOREIGN KEY (client_uuid) REFERENCES users (uuid)
)
The error:
ERROR: column userentity0_.client_uuid does not exist
Any idea what I'm doing wrong?
It's working as expected. The client_uuid is something which needs to be in user table. As one manager will be having multiple clients, you can't insert multiple client_uuid in manager table single field. But when it comes the other way around than one user will be having only one manager. So they can store the manager_uuid easily.
You can try make your code like below:
CREATE TABLE IF NOT EXISTS managers
(
uuid uuid DEFAULT gen_random_uuid() PRIMARY KEY,
manager_uuid uuid NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
FOREIGN KEY (manager_uuid) REFERENCES users (uuid)
)
CREATE TABLE IF NOT EXISTS users
(
uuid uuid DEFAULT gen_random_uuid() PRIMARY KEY,
client_uuid uuid NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
FOREIGN KEY (client_uuid) REFERENCES managers(uuid)
)
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 a notificationTypes table, permission table. these are linked by the help of bridge table notification_types_permission.
When all the entities are new, it works fine.
My issue is that when I try to insert new notificationTypes with existing permission, that time permission also tries to insert a new one.
My table and entity structures are given below.
CREATE TABLE `notification_types` (
`Id` BIGINT(16) NOT NULL AUTO_INCREMENT,
`TypeName` VARCHAR(80) NOT NULL,
`InsertedDttm` DATETIME NULL,
`InsertedBy` BIGINT(16) NULL,
`UpdatedDttm` DATETIME NULL,
`UpdatedBy` BIGINT(16) NULL,
PRIMARY KEY (`Id`));
CREATE TABLE `notification_types_permission` (
`Id` BIGINT(16) NOT NULL AUTO_INCREMENT,
`NotificationTypes_ID` BIGINT(16) NOT NULL,
`permissions_ID` int(11) NOT NULL,
PRIMARY KEY (`Id`), FOREIGN KEY (NotificationTypes_ID)
REFERENCES `frontoffice`.notification_types(id), FOREIGN KEY (permission_Id)
REFERENCES `frontoffice`.`permission`(id));
public class NotificationTypes {
#Id
private Long id;
private String typeName;
#Temporal(TemporalType.TIMESTAMP)
private Date insertedDttm;
private Long insertedBy;
#Temporal(TemporalType.TIMESTAMP)
private Date updatedDttm;
private Long updatedBy;
#OneToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private List<RLPermissions> permissions;
}
#OneToMany( fetch = FetchType.EAGER)
private List<RLPermissions> permissions;
avoid cascade property from that relationship if there is no need to delete, update and insert operation for permissions list.
Retrieve the new Permission first:
Permission p = permissionDao.getPermissionByName("SuperAdmin");
Then add it like this:
notificationType.notificationTypesPermission.getPermissions().add(p)
you have set cascade=CascadeType.PERSIST which will update the many side of the relationship. Any change to the NotificationTypes will update the corresponding child entities
CascadeType.PERSIST : means that save() or persist() operations cascade to related entities.
I can't make MySQL to update a TIMESTAMP field marked as CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP when the update query is made by Hibernate.
According to this answer, and this comment, MySQL should handle tables with more than one column defaulting to CURRENT_TIMESTAMP. My problem is that, when updating the entities with Hibernate, that seems to work for one table, but not for another in the same schema. Also, it works fine for all tables when I run the updates directly against the DB (without Hibernate).
CREATE TABLE `homes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userId_fk` int(11) NOT NULL,
`type_fk` varchar(45) NOT NULL,
`summary` varchar(255) DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `fk_homes_users` (`userId_fk`),
KEY `fk_homes_homeTypes_idx` (`type_fk`),
CONSTRAINT `fk_homes_homeTypes` FOREIGN KEY (`type_fk`) REFERENCES `homeTypes` (`type`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_homes_users` FOREIGN KEY (`userId_fk`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
CREATE TABLE `addresses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`street` varchar(45) DEFAULT NULL,
`city` varchar(45) NOT NULL,
`province` varchar(45) DEFAULT NULL,
`postcode` varchar(45) NOT NULL,
`country` varchar(45) NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `fk_addresses_countries_idx` (`country`),
CONSTRAINT `fk_addresses_countries` FOREIGN KEY (`country`) REFERENCES `countries` (`iso3166_alpha3`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8;
The second table, addresses, has its updated column correctly changed with each update. The first one doesn't. I'm using MySQL 5.7 and Hibernate 4.3.11.
My entities:
#Entity
#Table(name = "homes")
public class Home {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull
#Column(name = "userId_fk", nullable = false)
private Integer userId;
#OneToOne(cascade = ALL)
#JoinTable(name = "homes_addresses", joinColumns = #JoinColumn(name = "homeId_fk"),
inverseJoinColumns = #JoinColumn(name = "addressId_fk"))
private Address address;
#Enumerated(EnumType.STRING)
#Column(name = "type_fk") // TODO nullable = false ?
private HomeType type;
private String summary;
private LocalDateTime created;
private LocalDateTime updated;
}
#Entity
#Table(name = "addresses")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String street;
#NotNull
private String city;
private String province;
#NotNull
private String postcode;
#NotNull
private String country;
private LocalDateTime created;
private LocalDateTime updated;
}
An example of query that does NOT trigger the ON UPDATE:
Hibernate:
/* update
xxx.xxx.Home */ update
homes
set
created=?,
summary=?,
type_fk=?,
updated=?,
userId_fk=?,
videoLink=?
where
id=?