StackOverflow has a topic Joincolumn vs mappedby very interesting:
@Entitypublic class Company { @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY) @JoinColumn(name = "companyIdRef", referencedColumnName = "companyId") private List<Branch> branches;...}
@Entitypublic class Company { @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY, mappedBy = "companyIdRef") private List<Branch> branches; ...}
What is the difference between the two types of cascading?
There are two high-score answers below the original question. Unfortunately for my this kind of slag, read or confused.
If you can't understand what people are writing, then you can tune in.
Prepare the Environment
AX, prepare the debugging environment first.
I am using Spring Boot 2.0 + Spring Data JPA 2.0, hibernate version is 5.2.14.
Start with Hibernate's debug options in spring Boot application.yml.
spring: jpa: properties: hibernate: show_sql: true # 打印SQL语句 format_sql: true # 格式化SQL语句 use_sql_comments: true # 增加注释信息,就知道语句对应的Entity类型了 generate_statistics: true # 统计信息,给出了每一步的耗时信息
Also log information at the level at which log is configured to open org.hibernate.type
debug
. I'm using log4j2, and I need to add it to the Log4j2.yml file:
Configuration: Loggers Logger: - name: org.hibernate.type additivity: false level: trace # 这个最关键 AppenderRef: - ref: CONSOLE - ref: ROLLING_FILE
First time Test
The original topic used is the "company-department" model, I here have a ready-made "departmental-employee" model, directly reused. The truth is the same.
@Entity @ Table (Name= "Department_demo" ) public Department { @Id @GeneratedValue (strategy = Generationt Ype. identity ) private Integer ID; private String name; @OneToMany (cascade = Cascadetype.) all , orphanremoval = true , fetch = Fetchtype. eager , Mappedby = "DepartmentID" ) private set<employee> Employeeset = new hashset<> (); //Setters & Getters }
@Entity@Table(name="employee_demo")publicclass Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; private Integer departmentId; // setters & getters}
Here, Department uses the primary key one id
-to-many properties associated with the employee departmentId
.
Then just save an object to see.
@Service Public classDepartmentservice {@AutowiredDepartmentrepository departmentrepository;@PostConstruct Public void Insertnewrecord() {Department Department =New Department(); Department.SetName("Leader"); Departmentrepository.Save(department); Employee emily =New Employee(); Emily.SetName("David"); Employee Alice =New Employee(); Alice.SetName("Wang Dali"); Department.AddEmployee(Emily); Department.AddEmployee(Alice); Departmentrepository.Save(department); }}
After startup, I found that it was printed in log.
2018-04-22 09:11:22,155:info Restartedmain (statisticalloggingsessioneventlistener.java:258)-Session Metrics {0 Nano Seconds spent acquiring 0 JDBC connections; 0 Nanoseconds spent releasing 0 JDBC connections; 0 Nanoseconds spent preparing 0 JDBC statements; 0 nanoseconds spent executing 0 JDBC statements; 0 nanoseconds spent executing 0 JDBC batches; 0 Nanoseconds spent performing 0 L2C puts; 0 Nanoseconds spent performing 0 l2c hits; 0 Nanoseconds spent performing 0 l2c misses; 0 Nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections); 0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 Collections)}hibernate:/* Inser T com.example.demo.model.PO.Department */INSERT INTO Department_demo (name) VALUES (?) 2018-04-22 09:11:22,314:trace Restartedmain (basicbinder.java:65)-binding parameter [1] as [VARCHAR]-[Leader]hibernat E: Select Currval (' department_demo_id_seq ') 2018-04-22 09:11:22,438:info restartedmain (statisticalloggingsessioneve ntlistener.java:258)-Session Metrics {733681 nanoseconds spent acquiring 1 JDBC connections; 0 Nanoseconds spent releasing 0 JDBC connections; 6711770 Nanoseconds spent preparing 2 JDBC statements; 75097150 Nanoseconds spent executing 2 JDBC statements; 0 nanoseconds spent executing 0 JDBC batches; 0 Nanoseconds spent performing 0 L2C puts; 0 Nanoseconds spent performing 0 l2c hits; 0 Nanoseconds spent performing 0 l2c misses; 14646304 Nanoseconds spent executing 1 flushes (flushing a total of 1 entities and 1 collections); 0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 Collections)}hibernate:/* Load Com.example.demo.model.PO.Department */Select Department0_.id as id1_1_1_, department0_.name as name2_1_1_, employeese1_.department_id as departme2_2_3_, Employeese1_.id as id1_2_3_, employeese1_.id as id1_2_0_, employeese1_.department_id as departme2_2_0_, Employe Ese1_.name as name3_2_0_ from Department_demo department0_ left outer join Employee_demo EMPLOYEESE1 _ on department0_.id=employeese1_.department_id where Department0_.id=?2018-04-22 09:11:22,460:trac E Restartedmain (basicbinder.java:65)-binding parameter [1] as [INTEGER]-[33]2018-04-22 09:11:22,499:trace RESTARTEDMA In (basicextractor.java:51)-Extracted value ([id1_2_0_]: [INTEGER])-[Null]2018-04-22 09:11:22,502:trace Restartedmai N (basicextractor.java:61)-Extracted value ([name2_1_1_]: [VARCHAR])-[leader]2018-04-22 09:11:22,503:trace restarted Main (basicextractor.java:51)-Extracted value ([departme2_2_3_]: [INTEGER])-[null]hibernate:/* Insert Com.exampl E.demo.model.po.employee */INSERT INTO Employee_demo (department_id, name) v Alues (?,?) 2018-04-22 09:11:22,516:trace Restartedmain (basicbinder.java:65)-binding parameter [1] as [INTEGER]-[33]2018-04-22 09:11 : 22,516:trace Restartedmain (basicbinder.java:65)-binding parameter [2] as [VARCHAR]-[David]hibernate:select Currval (' Employee_demo_id_seq ') Hibernate:/* Insert Com.example.demo.model.PO.Employee */Insert int o Employee_demo (department_id, name) VALUES (?,?) 2018-04-22 09:11:22,526:trace Restartedmain (basicbinder.java:65)-binding parameter [1] as [INTEGER]-[33]2018-04-22 09 : 11:22,527:trace Restartedmain (basicbinder.java:65)-binding parameter [2] as [VARCHAR]-[Wang Dali]hibernate:sele CT currval (' employee_demo_id_seq ')
Second Test
The second time the Cascade annotations of the department were modified.
@Entity@Table(name="department_demo")public class Department { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) @JoinColumn(name="departmentId", referencedColumnName = "id") private Set<Employee> employeeSet = new HashSet<>();
The printed log is like this.
2018-04-22 09:16:29,366:info Restartedmain (statisticalloggingsessioneventlistener.java:258)-Session Metrics {0 Nano Seconds spent acquiring 0 JDBC connections; 0 Nanoseconds spent releasing 0 JDBC connections; 0 Nanoseconds spent preparing 0 JDBC statements; 0 nanoseconds spent executing 0 JDBC statements; 0 nanoseconds spent executing 0 JDBC batches; 0 Nanoseconds spent performing 0 L2C puts; 0 Nanoseconds spent performing 0 l2c hits; 0 Nanoseconds spent performing 0 l2c misses; 0 Nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections); 0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 Collections)}hibernate:/* Inser T com.example.demo.model.PO.Department */INSERT INTO Department_demo (name) VALUES (?) 2018-04-22 09:16:29,478:trace Restartedmain (basicbinder.java:65)-binding parameter [1] as [VARCHAR]-[Leader]hibernat E: Select Currval (' department_demo_id_seq ') 2018-04-22 09:16:29,513:info restartedmain (statisticalloggingsessioneve ntlistener.java:258)-Session Metrics {743154 nanoseconds spent acquiring 1 JDBC connections; 0 Nanoseconds spent releasing 0 JDBC connections; 4202462 Nanoseconds spent preparing 2 JDBC statements; 9628594 Nanoseconds spent executing 2 JDBC statements; 0 nanoseconds spent executing 0 JDBC batches; 0 Nanoseconds spent performing 0 L2C puts; 0 Nanoseconds spent performing 0 l2c hits; 0 Nanoseconds spent performing 0 l2c misses; 11708469 Nanoseconds spent executing 1 flushes (flushing a total of 1 entities and 1 collections); 0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 Collections)}hibernate:/* Load Com.example.demo.model.PO.Department */Select Department0_.id as id1_1_1_, department0_.name as name2_1_1_, employeese1_.department_id as departme2_2_3_, employeese1_.id as id1_2_3_, employeese1_.id as id1_2_0_, employeese1_.department_id as departme2_2_0_, employee Se1_.name as name3_2_0_ from Department_demo department0_ left outer join Employee_demo Employeese1_ On department0_.id=employeese1_.department_id where Department0_.id=?2018-04-22 09:16:29,529:trace Restartedmain (BASICBINDER.JAVA:65)-binding parameter [1] as [INTEGER]-[34]2018-04-22 09:16:29,538:trace Restartedmai N (basicextractor.java:51)-Extracted value ([id1_2_0_]: [INTEGER])-[Null]2018-04-22 09:16:29,541:trace Restartedmain (basicextractor.java:61)-Extracted value ([name2_1_1_]: [VARCHAR])-[Leader]2018-04-22 09:16:29,542:trace RestartedM Ain (basicextractor.java:51)-Extracted value ([departme2_2_3_]: [INTEGER])-[null]hibernate:/* Insert Com.example . demo.model.PO.Employee */INSERT INTO Employee_demo (department_id, name) va Lues (?,?) 2018-04-22 09:16:29,552:trace Restartedmain (basicbinder.java:65)-binding parameter [1] as [INTEGER]-[34]2018-04-22 09:16: 29,552:trace Restartedmain (basicbinder.java:65)-binding parameter [2] as [VARCHAR]-[Wang dali]hibernate:select Currval (' Employee_demo_id_seq ') Hibernate:/* Insert Com.example.demo.model.PO.Employee */insert Into Employee_demo (department_id, name) VALUES (?,?) 2018-04-22 09:16:29,555:trace Restartedmain (basicbinder.java:65)-binding parameter [1] as [INTEGER]-[34]2018-04-22 09 : 16:29,556:trace Restartedmain (basicbinder.java:65)-binding parameter [2] as [VARCHAR]-[David]hibernate:select Currval (' Employee_demo_id_seq ') Hibernate:/* Create One-to-many Row Com.example.demo.model.PO.Department.employe ESet */update employee_demo set department_id=? where Id=?2018-04-22 09:16:29,576:trace restartedmain (basicbinder.java:65)-binding parameter [1] as [INTEGER]-[34]2018-04-22 09:16:29,577:trace Restartedmain (basicbinder.java:65)-binding parameter [2] as [INTE GER]-[30]hibernate:/* Create One-to-many Row Com.example.demo.model.PO.Department.employeeSet */Update Empl Oyee_demo set department_id=? where Id=?2018-04-22 09:16:29,595:trace restartedmain (basicbinder.java:65)-binding parameter [1] as [INTEGER]- [34]2018-04-22 09:16:29,596:trace Restartedmain (basicbinder.java:65)-binding parameter [2] as [INTEGER]-[29]2018-04- 09:16:29,600:info Restartedmain (statisticalloggingsessioneventlistener.java:258)-Session Metrics {225339 nanosec Onds spent acquiring 1 JDBC connections; 0 Nanoseconds spent releasing 0 JDBC connections; 731494 Nanoseconds spent preparing 7 JDBC statements; 24985288 Nanoseconds spent executing 7 JDBC statements; 0 nanoseconds spent executing 0 JDBC batches; 0 Nanoseconds spent performing 0 L2C puts; 0 Nanoseconds spent performing 0 l2c HIts 0 Nanoseconds spent performing 0 l2c misses; 35806114 Nanoseconds spent executing 1 flushes (flushing a total of 3 entities and 1 collections); 0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)}
Simple analysis
Comparing log, we can see that two times when a new employee object is persisted, it will:
- Executes a LEFT JOIN query statement, querying all employee objects associated with the department
- Each new Employee object performs an insert operation
The difference is that when you use JoinColumn
annotations for the second time, log displays: each new Employee object executes an UPDATE statement and updates the foreign key .
The reason is that the mappedBy
assignment operation of the foreign key is delegated to the Employee object. Instead, JoinColumn
you choose to constrain the association of the foreign key by the Department object itself.
There are only a few differences between the two annotations, but the final result varies greatly. More write operations, in the production environment is easy to make a great pressure on the database. departmentId
It is clearly a more appropriate scenario to complete the assignment of the Employee object properties in your code.
In fact, the above two scenarios are OneToMany
Example 2, example 3, annotated in the JPA API documentation.
Two-way annotations of example 1 can also avoid redundant write operations.
Resources
- [JAVAEE-JPA] Performance optimization: How to locate performance issues
- The best of the @OneToMany relationship with JPA and Hibernate
Joincolumn vs Mappedby