I want to set a default value for my forein key status_id.
So I created a trigger inside Oracle, which sets a default value on insert. After that, Hibernate should update the linked foreign instance.
I have following Database in Oracle:
CREATE TABLE status (
status_id NUMBER(10) NOT NULL,
name VARCHAR2(50) NOT NULL,
CONSTRAINT status_pk PRIMARY KEY (status_id)
);
CREATE TABLE customer (
customer_id NUMBER(10) NOT NULL,
street VARCHAR2(50),
zip VARCHAR2(50),
city VARCHAR2(50),
salutation VARCHAR2(50),
title VARCHAR2(50),
firstname VARCHAR2(50),
surname VARCHAR2(50),
phone VARCHAR2(50),
fax VARCHAR2(50),
email VARCHAR2(50),
type VARCHAR2(50),
status_id NUMBER(10) NOT NULL,
CONSTRAINT customer_pk PRIMARY KEY (customer_id),
CONSTRAINT customer_status_fk FOREIGN KEY (status_id) REFERENCES status(status_id)
);
INSERT INTO status (status_id, name) VALUES ('1', 'DEFAULT');
INSERT INTO status (status_id, name) VALUES ('2', 'PREMIUM');
INSERT INTO status (status_id, name) VALUES ('3', 'SUPPLIER');
I want to set status to 'DEFAULT' if not specified when inserting a new customer. So I created following Trigger:
CREATE OR REPLACE TRIGGER status_default_trigger BEFORE INSERT ON customer FOR EACH ROW BEGIN
IF :NEW.type IS NULL THEN
SELECT 'NEW' INTO :NEW.type FROM DUAL;
END IF;
IF :NEW.status_id IS NULL THEN
SELECT status_id INTO :NEW.status_id FROM status WHERE status.status_id = 1;
END IF;
END;
/
My Entities looks as follows:
Status.java:
#Data
#Entity
#Table(name = "status")
public class Status {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private BigDecimal statusId;
#OneToMany(mappedBy = "status", fetch = FetchType.LAZY)
private List<Customer> customerList;
#Column
private String name;
}
Customer.java:
#Data
#Entity(name = "einsender")
#Table(name = "einsender")
public class Einsender {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private BigDecimal einsenderId;
#Generated(GenerationTime.INSERT) // This fails to start Hibernate entityManager
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "status_id")
private Status status;
#Column
private String street;
#Column
private String zip;
#Column
private String city;
#Column
private String salutation;
#Column
private String title;
#Column
private String firstname;
#Column
private String surname;
#Column
private String phone;
#Column
private String fax;
#Column
private String email;
#Generated(GenerationTime.INSERT) // This works instead
#Column
private String type;
}
I expect that Customer.Status will be default Status(statusId=1,name='DEFAULT); But I get following Error:
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 javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Unknown entity: de.example.model.Status
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1762) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
...
Caused by: org.hibernate.MappingException: Unknown entity: de.bund.bamf.vup.service.vorgang.model.Status
By the fact, the Customer.type Value will be mapped if I comment #Generated(GenerationTime.INSERT) above private Status status;
Related
I get this error when requesting service line:
List<Order> orders = ordersRepository.getByCustomerId(id);
Error:
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [5] did not match expected type [nz.webshop.models.Customer.Customers (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [5] did not match expected type [nz.webshop.models.Customer.Customers (n/a)]
Repository:
public interface OrdersRepository extends JpaRepository<Order, Integer> {
List <Order> getByCustomerId(Integer customerId);
}
Entities:
#EntityA:
#Table(name = "orders")
public class Order {
#ManyToOne(targetEntity=Customers.class)
#JoinColumn (name = "customer_id", referencedColumnName="customer_id")
private Integer customerId;
...getters/setters
#EntityB:
#Table (name ="customer")
public class Customers {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "customer_id")
private Integer customerId;
#OneToMany(targetEntity = Order.class, mappedBy = "customerId")
private List<Order> Order;
...getters/setters
Where tables are like this:
CREATE TABLE orders
(
order_id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT,
FOREIGN KEY (customer_id) REFERENCES customer (customer_id)
);
CREATE TABLE customer
(
customer_id INT AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(50) ,
last_name VARCHAR(50)
);
Update:
I changed EntityA as follows:
#EntityA:
#Table(name = "orders")
public class Order {
#Column(name = "customer_id")
private Integer customerId;
#ManyToOne(targetEntity=Customers.class)
private Customer customer;
...getters/setters
But then when requesting the same service line new error: org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet. Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'order0_.customer_customer_id' in 'field list'.
Where is the problem could be?
It looks like the Order.customerId property is not set up correctly. You're requesting that ManyToOne find Customer models based on their IDs, but then you're loading the models as Integers. Try this instead:
#ManyToOne
#JoinColumn(name = "customer_id", referencedColumnName="customer_id")
private Customer customer;
Let me know if this works, and if not, we can troubleshoot some more.
I have this model
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToOne
#PrimaryKeyJoinColumn
private Address registred_address_id;
#OneToOne
#PrimaryKeyJoinColumn
private Address actual_address_id;
...
And I have this method
private Customer addCustomer(String first_name, String last_name, String middle_name, String sex, Address address) {
Customer customer = new Customer(first_name, last_name, middle_name, sex);
customer.setActual_address_id(address);
customer.setRegistered_address_id(address);
customerRepo.save(customer);
return customer;
}
But hibernate not set actual_address_id and registered_address_id (it,s OneToOne)
Hibernate:
insert
into
customer
(first_name, last_name, middle_name, sex)
values
(?, ?, ?, ?)
2021-03-18 14:01:58.340 WARN 12836 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 23502
2021-03-18 14:01:58.340 ERROR 12836 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : ОШИБКА: значение NULL в столбце "registred_address_id" отношения "customer" нарушает ограничение NOT NULL
Подробности: Ошибочная строка содержит (6, null, null, null, null, null, male).
You mapping is contradictory.
From one side you want Customer.id to be generated by database after its insert. This is what GenerationType.IDENTITY actually mean.
From another side you try to use #PrimaryKeyJoinColumn what mean Customer.id should be set by Address.id. The correct mapping for #PrimaryKeyJoinColumn should look something like this:
#Entity
public class Customer {
#Id
private long id;
#OneToOne
#PrimaryKeyJoinColumn
private Address address;
public void setAddress(Address address) {
this.address = address;
this.id = address.getId();
}
// ...
}
but this will not fit your requirements.
So, I would suggest you correct your customer table like this:
create table customer
(
id bigserial not null,
reg_address_id bigint,
act_address_id bigint,
-- ...
primary key(id),
foreign key(reg_address_id) references address(id),
foreign key(act_address_id) references address(id)
);
and then use the following mapping:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne
#JoinColumn(name = "reg_address_id")
private Address registredAddress;
#OneToOne
#JoinColumn(name = "act_address_id")
private Address actualAddress;
// ...
}
Simple issue here: I'm running Spring Boot 2.2.5 on a mySQL database with MySQL5Dialect. Everything was peachy until I've added #ManyToOne annotation to Slide entity referencing the User entity - now Hibernate can't create tables because it creates the users table and then tries to alter slides table which it hasn't created yet. What did I do wrong?
User:
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private boolean enabled;
private String token;
private String username;
private String password;
private String role;
private String name;
private String surname;
private String email;
private boolean emailVisible;
private String phone;
private boolean phoneVisible;
private int cohortNumber;
private String company;
private String position;
private String linkedIn;
private String function;
private String bio;
private String membership;
private Date membershipSince;
private Date membershipUntil;
}
Slide:
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString(exclude = "editor")
#Table(name = "slides")
public class Slide {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private boolean visible;
private int order;
private Date edited;
#ManyToOne
#JoinColumn(name = "editor_id")
private User editor;
private String title;
private String text;
private String picture;
}
Hibernate config specifics:
spring.jpa.hibernate.ddl-auto = update
spring.datasource.initialization-mode=always
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
And the error:
Hibernate: create table users (id integer not null auto_increment, bio varchar(255), cohort_number integer not null, company varchar(255), email varchar(255), email_visible bit not null, enabled bit not null, function varchar(255), linked_in varchar(255), membership varchar(255), membership_since datetime, membership_until datetime, name varchar(255), password varchar(255), phone varchar(255), phone_visible bit not null, position varchar(255), role varchar(255), surname varchar(255), token varchar(255), username varchar(255), primary key (id)) engine=MyISAM
Hibernate: alter table slides add constraint FKobqxptfxg36ls278o63ouq369 foreign key (editor_id) references users (id)
2020-08-11 14:27:48.201 WARN 8224 --- [ restartedMain] o.h.t.s.i.ExceptionHandlerLoggedImpl : GenerationTarget encountered exception accepting command : Error executing DDL "alter table slides add constraint FKobqxptfxg36ls278o63ouq369 foreign key (editor_id) references users (id)" via JDBC Statement
...
Caused by: java.sql.SQLSyntaxErrorException: Table '32293814_alumnicemba.slides' doesn't exist
Found the problem:
private int order;
"order" is not allowed as a field name here and Hibernate was encountering an error when trying to create the slides table but not logging that error. I've renamed the field to "sorting" and it works now.
I want to poulate ManyToMany table in my H2 database for test. I have Entities with many to many relationship. I know how to insert table "sessions" and table "speakers", but i dont know how to insert table "session_speakers":
#Entity
#Table(name = "sessions")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Session {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "session_id")
private Long sessionId;
#Column(name = "session_name")
private String sessionName;
#Column(name = "session_description")
private String sessionDescription;
#Column(name = "session_length")
private Integer sessionLength;
#ManyToMany
#JoinTable(
name = "session_speakers",
joinColumns = #JoinColumn(name = "session_id"),
inverseJoinColumns = #JoinColumn(name = "speaker_id"))
private List<Speaker> speakers;
and
#Entity
#Table(name = "speakers")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Speaker {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "speaker_id")
private Long speakerId;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "title")
private String title;
#Column(name = "company")
private String company;
#Column(name = "speaker_bio")
private String speakerBio;
#ManyToMany(mappedBy = "speakers")
#JsonIgnore
private List<Session> sessions;
My H2JpaConfig
#Configuration
#EnableTransactionManagement
public class H2JpaConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
.setName("DataBaseTestConfig" + ZonedDateTime.now() + UUID.randomUUID()).build();
}
}
my application-h2.properties:
hibernate.connection.driver_class=org.h2.Driver
spring.jpa.hibernate.ddl-auto=create\r\n
spring.jpa.database=h2
spring.datasource.username=test
spring.datasource.password=password
logging.level.org.hibernate.SQL=debug
logging.level.org.hibernate.type.descriptor.sql=trace
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.connection.url=jdbc\:h2\:mem\:testdb;;
spring.jpa.generate-ddl=true
and my data.sql for tests:
INSERT INTO sessions (session_name,session_length,session_description)
VALUES ('Keynote - The Golden Age of Software',45,''),
('A Better Way to Access Data with Spring Data',60,''),
('A Deep Dive Into Spring IoC',60,'') ...
INSERT INTO speakers (first_name,last_name,title,company,speaker_bio)
VALUES ('Sergio','Becker','Senior Developer','MicroOcean Software','Test'),
('James','Lowrey','Solutions Architect','Fabrikam Industries','Test') ...
INSERT INTO session_speakers (session_id,speaker_id)
VALUES (1,40),
(2,4),
(3,5)...
when i run my tests i got error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #6 of URL [file:/C:/work/JIRA_work/nauka/spring_project/ps-spring-data-jpa/conference-demo/target/test-classes/data.sql]: INSERT INTO session_speakers (session_id,speaker_id) VALUES (1,40), (2,4), ... nested exception is org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Referential integrity constraint violation: "FKBSD81C224TLAEPMSBQIWO3OBG: PUBLIC.SESSION_SPEAKERS FOREIGN KEY(SESSION_ID) REFERENCES PUBLIC.SESSIONS(SESSION_ID) (82)"; SQL statement:
INSERT INTO session_speakers (session_id,speaker_id) VALUES (1,40), (2,4) ...
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:603) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at
I would like to know, how to properly populate ManyToMany table.
You are referencing non-existent IDs.
You should either insert IDs manually (not relying on them being generated by the database) in the first two tables and reference those later.
INSERT INTO sessions (speaker_id, session_name,session_length,session_description)
VALUES (1, 'Keynote - The Golden Age of Software',45,''),
(2, 'A Better Way to Access Data with Spring Data',60,''),
(3, 'A Deep Dive Into Spring IoC',60,'') ...
INSERT INTO speakers (session_id, first_name,last_name,title,company,speaker_bio)
VALUES (11, 'Sergio','Becker','Senior Developer','MicroOcean Software','Test'),
(12, 'James','Lowrey','Solutions Architect','Fabrikam Industries','Test') ...
INSERT INTO session_speakers (session_id,speaker_id)
VALUES (1,11),
(2,12),
(3,11)...
Or you can populate the first two tables the way you do it now, but than you have to find the correct IDs when inserting into the junction table.
INSERT INTO session_speakers (session_id,speaker_id)
VALUES (
(SELECT session_id FROM sessions WHERE session_name = 'Keynote - The Golden Age of Software'),
(SELECT speaker_id FROM speakers WHERE last_name = 'Becker'))...
This way may not be the right one in your situation, but there are circumstances where it can be appropriate.
This is my sql table structure:
create table TBL_EMPLOYEE_FIVE(
EMP_ID integer generated always as identity(start with 50, increment by 4),
NAME varchar(50),
COUNTRY varchar(50),
MGR_ID integer,
MGR_COUNTRY varchar(50),
constraint PK_COMPOSIT_001AD primary key(EMP_ID,COUNTRY),
constraint FK_COMPO_00123 foreign key(MGR_ID,MGR_COUNTRY) references TBL_EMPLOYEE_FIVE
)
And this is my entity mapping:
#Entity
#Table(name="TBL_EMPLOYEE_FIVE")
#IdClass(EmployeeId.class)
public class EmployeeOne implements Serializable{
public EmployeeOne(){}
public EmployeeOne(String employeeName,String empCountry){
this.empCountry = empCountry;
this.employeeName = employeeName;
}
public EmployeeOne(String employeeName,String empCountry,EmployeeOne manager){
this.empCountry = empCountry;
this.employeeName = employeeName;
this.manager = manager;
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="EMP_ID")
private Integer employeeId;
#Id
#Column(name="COUNTRY")
private String empCountry;
#Column(name="NAME")
private String employeeName;
#ManyToOne( cascade= {CascadeType.PERSIST, CascadeType.PERSIST},
fetch= FetchType.LAZY,
targetEntity=EmployeeOne.class)
#JoinColumns({
#JoinColumn(name="MGR_ID",referencedColumnName="EMP_ID"),
#JoinColumn(name="MGR_COUNTRY",referencedColumnName="COUNTRY")
})
private EmployeeOne manager;
#OneToMany(cascade={CascadeType.PERSIST, CascadeType.PERSIST},mappedBy="manager")
private Set<EmployeeOne> employees;
// getters and setters,
}
This is the the embedded id mapping,
#Embeddable
public class EmployeeId implements Serializable{
public EmployeeId(){}
public EmployeeId(Integer employeeId,String empCountry){
this.employeeId = employeeId;
this.empCountry = empCountry;
}
#Column(name="EMP_ID")
private Integer employeeId;
#Column(name="COUNTRY")
private String empCountry;
// only getters and implementation of hashcode and equals method
}
And this is what I am trying to run in my main method:
EmployeeOne manager = new EmployeeOne("Yousuf Ahmadinejad", "IRAN");
em.persist(manager);
But here i am getting an exception i.e.
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: Attempt to modify an identity column 'EMP_ID'.
It's not like i didn't understood the exception,
but why this exception occured in the first place? I already annotated it with #GenerateValue for Empid and I am not setting the empId manually. Does this exception occur because I have combined primary key as empId and country, and than the empId is autogenerated using Identity, hence its giving an exception ?
Can you please tell me whats going wrong
One more thing i want to add here is, if i removed #Column and #Embeddeble annotation for EmployeeId.java, and than run, i get an following exception,
Caused by: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of com.entities.derived.EmployeeId.employeeId
So just trying to find the solution to persist employee keeping the autogenerated Id as it is
First Hibernate does not generate id's for composite keys, so you should change EmployeeOne to:
#Id
//#GeneratedValue(strategy=GenerationType.IDENTITY) remove this line
#Column(name="EMP_ID")
private Integer employeeId;
Second that's not how you should implement EmployeeId composite key class. See: https://stackoverflow.com/a/3588400/1981720
Third, the exception is thrown by the database, not Hibernate. Check if you're getting the same exception with another database.