The main research in this article is Hibernate's Session_flush and isolation level. The specific introduction and examples are as follows.
Let's look at some concepts first:
1. Dirty reading: Dirty reading is also called the reading of invalid data. It means that during database access, thing T1 modifies a certain value, and then thing T2 reads the value. After that, T1 cancels the modification of the value for some reason, which causes the data read by T2 to be invalid. Dirty reading means that when a thing is accessing data and modifying the data, and this modification has not been submitted to the database, another thing also accesses this data and then uses this data. Because this data is still not submitted, the data read by another thing is dirty data, and the operations performed based on the dirty data are incorrect.
2. Not repeated reading: For example, when I was reading a post, the data I found were Zhang San and Li Si. Then, after I refreshed, I found that the initial Zhang San became Zhang Ba. This is the so-called non-repeatable reading, because the data I read was not repeated.
3. Fantasy reading: When I was looking up the data, I started to find 3 records. When I refreshed it, I found that the records became 8. This is Fantasy reading.
4. Submit reading: You can only read after submitting. Oracle defaults to this. There is no dirty reading in this way.
5. Repeatability: It is obviously the opposite of non-repeatable reading. It can avoid non-repeatable reading, but this cannot avoid phantom reading.
6. Serialization: This method is very strict. In layman's terms, when I'm doing something, no one else can do it. It's very safe, but it's extremely inefficient.
Below we use practical examples to understand the application of Hibernate cache clearance.
Hibernate mapping database is related to the primary key generation strategy.
Examples of generating primary keys in UUID:
public class User {private String uid;private String uname;private Date birthday;public String getUid() {return uid;}public void setUid(String uid) {this.uid = uid;}public String getUname() {return uname;}public void setUname(String uname) {this.uname = uname;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}}User.hbm.xml:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- package represents the package name of the entity class --> <hibernate-mapping package="com.lixue.bean"> <!-- The name of the class node represents the class name of the entity, and the table represents the name of the entity mapped to the table in the database --> <class name="User" table="t_user"> <id name="uid"> <!-- Generate through UUID--> <generator/> </id> <property name="uname"/> <property name="birthday"/> </class> </hibernate-mapping>
Test method:
/** * Test the uuid primary key generation strategy*/ public void testSave1(){ /*Defined Session and Things*/ Session session = null; Transaction transaction = null; try { /*Get session and Things*/ session = HibernateUtils.getSession(); transaction = session.beginTransaction(); /*Create user*/ User user = new User(); user.setUname("Xi Jinping"); user.setBirthday(new Date()); /** * Because the primary key generation strategy of User is uuid, after calling save, it just includes the User in Session Management* The insert statement will not be issued, but the ID has been generated, and the existencesInDatebase status in PersistenceContext is false */ session.save(user); /** * Calling flush, Hibernate will clean up the cache (insert the objects in the temporary collection in session->insertions into the database, clearing the temporary collection) * At this time, the data cannot be seen in the database, but if the isolation level of the database is set to not submitted to read, * Then we can see the flushed data, and the existencesInDatabase status in PersistenceContext is true */ session.flush(); /** * Submit things* By default, the commit operation will perform flush cleaning cache, * So the data cannot be rolled back after calling flush without display * commit * transaction.commit(); } catch (Exception e) { e.printStackTrace(); transaction.rollback(); } finally{ HibernateUtils.closeSession(session); } } We can debug the program through breakpoints:
1. Since the side rate of the primary key generation of User is UUID, after calling the save() method, the User object can only be included in the Session management, and the insert statement will not be issued, but the ID has been generated (Note: Two places are very important after saving. First, there is an element in the session->actionQueue->insertions->elementData array that stores our object. This is a temporary collection object. Another is that PersistenceContext->EntityEntries->map->table->A certain array element->value stores the object. There is another attribute under the value, that is, existsInDatabase represents whether there is corresponding data in the database). As shown in the picture:
2. After calling the flush() method, the temporary stored value of actionQueue in the session will be cleared, and then the value of existsInDatabase in the PersistenceContext is set to true, indicating that there is corresponding data in the database at this time, but when opening the database and opening the table, you cannot see the data, because the default isolation level of our MySQL database is commit read, that is, you must submit to read the data. After calling the commit() method, there is data in the database.
Example of generating primary keys in native way:
public class User1 {private Integer uid;private String uname;private Date birthday;public Integer getUid() {return uid;}public void setUid(Integer uid) {this.uid = uid;}public String getUname() {return uname;}public void setUname(String uname) {this.uname = uname;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}}User1.hbm.xml:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- package represents the package name of the entity class --> <hibernate-mapping package="com.lixue.bean"> <!-- class node name represents the class name of the entity (remember to modify the class name when assigning the mapping file, otherwise a bug will occur), table represents the name of the entity mapped to the table in the database --> <class name="User1" table="t_user1"> <id name="uid"> <!-- Self-growth--> <generator/> </id> <property name="uname"/> <property name="birthday"/> </class> </hibernate-mapping>
Test method:
/** * Test native primary key generation strategy*/ public void testSave2(){ /*Defined Session and Things*/ Session session = null; Transaction transaction = null; try { /*Get session and Things*/ session = HibernateUtils.getSession(); transaction = session.beginTransaction(); /*Create user*/ User1 user = new User1(); user.setUname("Li Keqiang"); user.setBirthday(new Date()); /** * Because the primary key generation strategy of User1 is native, after calling Session.save(), the insert statement will be executed, and the temporary collection object will be cleared* Return the ID generated by the database, included in the Session management, and modified the existencesInDatabase status in the Session to true, * If the isolation level of the database is set to not submitted to read, then we can see the saved data*/ session.save(user); transaction.commit(); } catch (Exception e) { e.printStackTrace(); transaction.rollback(); } finally{ HibernateUtils.closeSession(session); } } Debug the program via breakpoint:
1. Since the primary key generation strategy is native, after calling the save() method, the insert statement will be executed, and the data in the temporary collection object will be cleared, and the ID generated by the database will be returned.
2. Include the object into session management, modify the existsInDatabase property in PersistenceContext to true (indicates that there is corresponding data in the database, but it cannot be seen because of the isolation area).
Let's test another method of Hibernate, which is evict(), which means expelling the object from the session.
For the program that generates a UUID primary key strategy, here is a test method:
/** * Test the uuid primary key generation strategy*/ public void testSave3(){ /*Defined Session and Things*/ Session session = null; Transaction transaction = null; try { /*Get session and Things*/ session = HibernateUtils.getSession(); transaction = session.beginTransaction(); /*Create user*/ User user = new User(); user.setUname("Hu Jintao"); user.setBirthday(new Date()); /** * Because the primary key generation strategy of User is uuid, after calling save, just incorporating the User into Session management* will not issue the insert statement, but the ID has been generated. The existsInDatebase status in the Session is false */ session.save(user); /*Eviction the user object from the session, that is, expelled from the EntityEntries property of the PersistenceContext*/ session.evict(user); /** * Cannot successfully submit, because when Hibernate cleans up the cache, the user object is taken out from the session inserts temporary collection for insert * After the operation, the existencesInDatabase in the entityEntries property needs to be updated to true, and we call the evil method* Eviction the user from the entityEntries of the session, so the existencesInDatabase property cannot be found, and it cannot be updated, throwing an exception */ transaction.commit(); } catch (Exception e) { e.printStackTrace(); transaction.rollback(); } finally{ HibernateUtils.closeSession(session); } } Debugging via breakpoints:
1. Since the primary key generation strategy of UUID is used, the insert statement will not be sent after calling the save() method. The object is included in the session management. The ID has been generated and there is no corresponding data in the database (that is, the existsInDatabase attribute value is false).
2. After calling evict(), expel the User object from the session, that is, expelled from the EntityEntries property of the PersistenceContext.
3. When I call the commit() method again, we will find that our data cannot be saved because at the beginning our existenceInDatabase property was false, that is, there is no corresponding data in the database. Then we called evil() to delete all the object properties in PersistenceContext (the existenceInDatabase property is also included), but the temporary stored data in actionQueue has not been deleted. When we call the commit() method, we will first implicitly call the flush() method. The function of this method has also been mentioned before. It will insert the temporary object in the actionQueue, and then set the existsInDatabase property value in the PersistenceContext to true. Unfortunately, there is no existsInDatabase property in the PersistenceContext, so an error will occur, resulting in the inability to save.
To do this, we improve the above procedure:
/** * Test the uuid primary key generation strategy*/ public void testSave4(){ /*Defined Session and Things*/ Session session = null; Transaction transaction = null; try { /*Get session and Things*/ session = HibernateUtils.getSession(); transaction = session.beginTransaction(); /*Create user*/ User user = new User(); user.setUname("Hu Jintao"); user.setBirthday(new Date()); /** * Because the primary key generation strategy of User is uuid, after calling save, just incorporating the User into Session management* will not issue the insert statement, but the ID has been generated. The existenceInDatebase status in PersistenceContext is false */ session.save(user); /** * After flush, Hibernate will clean the cache, save the user object to the database, clear the user object in the insertions in the session*, and set the status of existsInDatabase in PersistenceContext to true */ session.flush(); /* Evict the user object from the session, that is, expelled from the EntityEntries property of PersistenceContext */ session.evict(user); /** * can be successfully submitted because Hibernate cannot be in the session insertions collection when cleaning up the cache* The user object was found (cleared when flush was called), so the insert statement will not be issued, nor the status of existsInDatabase in the session will not be updated*/ transaction.commit(); } catch (Exception e) { e.printStackTrace(); transaction.rollback(); } finally{ HibernateUtils.closeSession(session); } } Note: After saving, we call the flush() method and then call the evict() method after the modified program.
Debugging via breakpoints:
1. Because it is still a UUID generation strategy, after calling save, the insert statement will not be issued, but the object is included in the session management. The existsInDatabase property in PersistenceContext is false.
2. After calling save(), we call the flush() method again. The function of this method is to clean the cache, that is, issue an insert statement, insert the temporary object in the insertions in the session into the database, then clear the temporary collection, and set the existsInDatabase property in the PersistenceContext to true.
3. After calling flush(), the evict() method is called. Its function is to clear the user object from the session, that is, to clear the EntityEntries property of the PersistenceContext.
4. After calling the evict() method, the commit() method will implicitly call the flush() method first. The function of flush is to clear the cache, that is, insert the object in the session->insertions temporary collection into the database, but we have called the flush() method before (Note: after calling this method, the temporary collection will be cleared), so the temporary collection has no objects at all, so the insert statement will not be issued. It will not update the existsInDatabase status in PersistenceContext. Submit successfully.
Let's consider using the evict() method in the native primary key generation strategy:
/** * Test native primary key generation strategy*/ public void testSave5(){ /*Defined Session and Things*/ Session session = null; Transaction transaction = null; try { /*Get session and Things*/ session = HibernateUtils.getSession(); transaction = session.beginTransaction(); /*Create user*/ User1 user = new User1(); user.setUname("Ma Ying-jeou"); user.setBirthday(new Date()); /** * Because the primary key generation strategy of User1 is native, after calling Session.save(), the insert statement will be executed, * Return the ID generated by the database, included in the Session management, modify the existencesInDatabase status in the Session to true, and clear the temporary set* If the isolation level of the database is set to not submitted to read, then we can see the saved data*/ session.save(user); /* Evict the user object from the session, that is, expelled from the EntityEntries property of the PersistenceContext*/ session.evict(user); /** * Can be successfully submitted because Hibernate is in the session insertions collection when cleaning cache* The user object cannot be found, so the insert statement will not be issued, nor the status of existsInDatabase in the session will not be updated*/ transaction.commit(); } catch (Exception e) { e.printStackTrace(); transaction.rollback(); } finally{ HibernateUtils.closeSession(session); } } Through debugging:
1. Since the primary key generation strategy is native, after calling the save method, an insert statement will be issued immediately, returning the ID generated by the database, incorporating the object into session management, modifying the existenceInDatabase property in PersistenceContext to true, that is, there is corresponding data in the database, and the objects in the temporary collection will be cleared. However, due to the MySQL isolation level, we cannot see the data before committing it.
2. After calling save, the object is called and the object is expelled from the session, that is, it is expelled from the EntityEntries in the PersistenceContext.
3. After calling the evict() method, the commit() method can be called successfully. The commit can be saved successfully, because before calling commit(), the flush() method will be called implicitly, that is, clean the cache and search for objects in the temporary collection to insert into the database. However, you will find that there is no data in the temporary collection, so the insert statement will not be issued, and the existenceInDatabase property in the PersistenceContext will not be updated.
Through the above cases, we can see that sometimes we need to call the flush() method to clean the cache. In addition, we also found a problem from the above, that is, when we save() the data, we cannot see the data before submitting it, that is, the isolation level of the database is limited. Now let’s talk about the isolation level of MySQL:
1. Check the current isolation level of the MySQL database:
select @@tx_isolation;
Note: From the figure, we can see that the default isolation level of the MySQL database is repeatable, which means that there will be no non-repeatable reading, that is, it must be submitted before it can be read.
2. Modify the current isolation level of MySQL (assuming that it is not submitted to read, that is, it can be read without commit):
set transaction isolation level read uncommited;
The above is all the detailed explanation of Hibernate's Session_flush and isolation level code. I hope it will be helpful to everyone. Interested friends can continue to refer to other related topics on this site. If there are any shortcomings, please leave a message to point it out. Thank you friends for your support for this site!