Interceptor
Speaking of Interceptor, I believe that children's shoes familiar with struts2 are definitely familiar with struts2. struts2 can customize the interceptor to perform a series of related work you want. And the Interceptor we are talking about here also has similar functions.
Without saying nonsense, just code:
The following is the MyInterceptor class, which implements the Interceptor interface:
public String onPrepareStatement(String arg0) { return arg0; } public boolean onSave(Object arg0, Serializable arg1, Object[] arg2, String[] arg3, Type[] arg4) throws CallbackException { if (arg0 instanceof User) { System.out.println("User to be saved=>"+((User)arg0).getName()); } return false; } I won’t read other methods, just follow the default implementation. We only need to change these two methods. We need to change the return value in onPrepareStatement to return the current SQL statement. The parameters are the executed SQL statement passed in. We can print out the statement by directly returning it.
In onSave, you can tell that it is called when saving. We can do a series of pre-preservation work.
I believe everyone can understand it by looking at the parameter names.
Serializable refers to the parameter of the sequence number, which refers to the attributes that map to the database ID.
Object[] This is a series of states, which has not been used much for the time being. I will study it later. However, the API explains that no matter how the value in this array is modified, the onSave method must return true.
String[] refers to the name of the attribute and Type[] is the type of the corresponding attribute.
1) This Interceptor can do some corresponding operations before and after saving the database. For example, if you want to modify the data and add prefix or suffix, you can use it to implement it. Let’s take a look at it below.
public boolean onSave(Object arg0, Serializable arg1, Object[] arg2, String[] arg3, Type[] arg4) throws CallbackException { if (arg0 instanceof User) { System.out.println("User to be saved=>"+((User)arg0).getName()); } //We add 123 as the prefix of the name here User user = (User)arg0; user.setName("123"+user.getName()); return false; }Let's take a look at the test method:
public static void main(String[] args) { Configuration cfg = new Configuration().configure(); SessionFactory sessionFactory = cfg.buildSessionFactory(); Interceptor interceptor = new MyInteceptor(); Session session = sessionFactory.openSession(interceptor); User user = new User(); user.setName("shun"); Transaction tx = session.beginTransaction(); session.save(user); tx.commit(); session.close(); } It's very simple, we just saved it simply. There are no mapping files and entity classes here, just try it out.
Run it and we can see:
User to be saved=>shun Hibernate: insert into USER (USER_NAME, age) values (?, ?) Hibernate: update USER set USER_NAME=?, age=? where USER_ID=?It will update the name and age at the end, mainly because we have made changes in the onSave method.
public boolean onLoad(Object arg0, Serializable arg1, Object[] arg2, String[] arg3, Type[] arg4) throws CallbackException { if (arg0 instanceof User) { System.out.println("User to be loaded=>"+(arg2[0]+":"+arg2[1])); } User user = (User)arg0; //Judge which attribute is name for (int i = 0; i < arg3.length; i ++){ if (arg3[i].equals("name")){ user.setName((String)arg2[i]).replace("123","")); arg2[i] = ((String)arg2[i]).replace("123",""); } } return false; } The value of the modified attribute when loading is written in the onLoad method.
The arg0 here is our User object. It has no value yet. This method is called after the load method, so it is useless for us to operate the user at this time, and the user.setName here is a useless operation. Mainly in:
arg2[i] = ((String)arg2[i]).replace("123","");
This code changes the value of the returned attribute, so the value in the user object we get in the program will also change. Let's run the test method to see:
public static void main(String[] args) { Configuration cfg = new Configuration().configure(); SessionFactory sessionFactory = cfg.buildSessionFactory(); Interceptor interceptor = new MyInteceptor(); Session session = sessionFactory.openSession(interceptor); User user = (User)session.load(User.class,new Long(39)); System.out.println("User name:"+user.getName()); session.close(); }Looking at the results, we got:
Hibernate: select user0_.USER_ID as USER1_0_0_, user0_.USER_NAME as USER2_0_0_, user0_.age as age0_0_ from USER user0_ where user0_.USER_ID=? User to be loaded=>123shun:0 User name:shun
We have removed the original 123 and carried out relevant processing after the actual loading, but this is not a real processing before the actual loading, and it is a bit suspicious of speculation. But it is also a consideration. Interceptor may be used the most in the relevant processing of logs. For example, we need to log correspondingly for each operation, so Interceptor is a good choice.
Collection
Remember the Set we used in one-to-many in the previous examples, do you still have the impression? If you don’t, go check the information and review it. Today we will learn around these collections.
Let's just get to the point.
1) First let’s learn Set. Everyone knows that there is also a Set in the JAVA util package. So what is the difference and connection between set and set in hibernate? We open the hibernate API, find Set, and you can see it.
What we see is the parent class of such a hibernate collection. It is an abstract class with a series of concrete implementation classes. When we continue to see the following method, we find that this class implements the encapsulation of the java collection, so we understand that the so-called hibernate Set actually only encapsulates the java set.
So, is this characteristic that does not allow duplicate elements in Set also in hibernate? The answer is of course yes.
We don't look at these here. In the past, when we learned mapping, we directly associated properties with the associated classes, but today we are not like this. We use another method, just associate a string to see if there is any problem.
But before looking at this question, let’s take a look at the String comparison in java.
What we see is the parent class of such a hibernate collection. It is an abstract class with a series of concrete implementation classes. When we continue to see the following method, we find that this class implements the encapsulation of the java collection, so we understand that the so-called hibernate Set actually only encapsulates the java set.
So, is this characteristic that does not allow duplicate elements in Set also in hibernate? The answer is of course yes.
We don't look at these here. In the past, when we learned mapping, we directly associated properties with the associated classes, but today we are not like this. We use another method, just associate a string to see if there is any problem.
But before looking at this question, let’s take a look at the String comparison in java.
public static void main(String[] args) { String s1 = "shun1"; String s2 = "shun1"; System.out.println("s1==s2:"+(s1==s2)); } I believe many children's shoes know that the answer is true.
Before making an example, let’s take a look at our mapping file. We won’t write the mapping classes:
This is the mapping file for TUser:
<class name="TUser" table="t_user" dynamic-insert="true" dynamic-update="true" dynamic-update="true"> <id name="id" column="id"> <generator /> </id> <property name="name" type="java.lang.String" column="name"/> <property name="age" type="java.lang.Integer" column="age"/> <set name="addresses" cascade="all" table="t_address"> <key column="user_id" /> <!-- <one-to-many/> --> <element column="address" type="string" /> </set> </class>
Next is the Address mapping file:
<class name="Address" table="t_address" dynamic-insert="false" dynamic-update="false"> <id name="id" column="id" type="java.lang.Integer"> <generator /> </id> <property name="address" column="address" type="java.lang.String" /> <many-to-one name="user" column="user_id" not-null="true"></many-to-one> </class>
The children's shoes have seen it clearly. I commented one-to-many in the Set in TUser and used element. No matter what the problem is, let's look at the database first:
This is the t_address table:
Here is the t_user table:
We can see that User with id 4 corresponds to three addresses. Next, let’s take a look at the test method:
public static void main(String[] args) { Configuration cfg = new Configuration().configure(); SessionFactory sessionFactory = cfg.buildSessionFactory(); Session session = sessionFactory.openSession(); TUser user = (TUser)session.load(TUser.class,new Integer(4)); Set set = user.getAddresses(); session.close(); System.out.println("address size:"+set.size()); } A very simple query class, just took out this result, we saw a strange phenomenon:
address size:1
This is the result!
You will definitely say, it must be wrong, it is a bug in hibernate. I must be happy here. I can finally submit a bug. When I switched jobs, I could say loudly that I submitted a bug for hibernate. Haha, but unfortunately, this is not a bug.
I just said that the comparison of the string we had in front of was paving the way here, so how to pave it?
We use Set in the configuration file and associate it through String characters. Then, when it is taken out in the database and put it into the Set, it will first determine whether the values of the associated character are equal. Here, since our values are equal (we will not dig into how it compares for the time being), we only need to know that when we use strings to compare, we fall into the string trap in JAVA again. If you find out that there is only one, then deletion is more troublesome when deleting it, it will delete all the same records.
Then let's take a look at the deleted one:
TUser user = (TUser)session.load(TUser.class,new Integer(4)); Transaction tx = session.beginTransaction(); Object obj = user.getAddresses().iterator().next(); user.getAddresses().remove(obj); tx.commit(); session.close();
The statement output by hibernate here is:
Hibernate: delete from t_address where user_id=?
I believe everyone knows when it is to delete all addresses under the user. There is no choice but to delete all of this.
So you need to pay attention to it in real development.
2) We talked about Set above, it seems that it is not very pleasant to use. There is such a trap, but there is no way. Set is the one we use the most, and generally no one will directly associate strings. But many people are still unhappy, so hibernate will have an extra Bag as required (maybe not as required, maybe some people in them are dissatisfied, haha).
Let's first look at its basic usage:
First, we need to modify the Set tag in the previous TUser mapping file to:
<bag name="addresses" lazy="true" table="t_address"> <key column="user_id" /> <element type="string" column="address" /> </bag>
And the corresponding entity class needs to modify the addresses type to List type.
Here we add three addresses:
We run the test code:
public static void main(String[] args) { Configuration cfg = new Configuration().configure(); SessionFactory sessionFactory = cfg.buildSessionFactory(); Session session = sessionFactory.openSession(); TUser user = (TUser)session.load(TUser.class,new Integer(4)); System.out.println("address size:"+user.getAddresses().size()); session.close(); }
Here we see:
address size:3
This time we can see it all, regardless of whether there are any repetitions or not.
But we just looked at a deletion problem. Bag has not been solved here, and we need to use idBag. We see the configuration file and need the following modifications:
idbag name="addresses" table="t_address" lazy="true"> <collection-id type="int" column="id"> <generator /> </collection-id> <key column="user_id" /> <element type="string" column="address" /> </idbag>
We see that it only has one more collection-id than bag to indicate the record number to be deleted.
When we rerun the deleted code:
TUser user = (TUser)session.load(TUser.class,new Integer(4)); Transaction tx = session.beginTransaction(); Object obj = user.getAddresses().iterator().next(); user.getAddresses().remove(obj); tx.commit();
We see that the output statement is:
Hibernate: delete from t_address where id=?
This time, it is not deleted through user_id, but based on the ID of t_address, which means it really deletes the record we need to delete.
We see the database and the record is now:
We have deleted the first record, it is correct.
3) After looking at the above two methods, let’s take a look at MAP. The biggest difference between it and the above two is that it can correspond to key values. Look directly at the code, intuitive point of view:
First, we need to modify the configuration file:
<map name="addresses" table="t_address" lazy="true"> <key column="user_id" /> <index type="string" column="type" /> <element type="string" column="address" /> </map>
The biggest difference between it and the previous two is that there is an index, which is equivalent to the map key in Java, and we use this to retrieve the corresponding records. Remember, after changing here, you need to change the corresponding entity class, and you need to change the type of the addresses attribute to Map.
Look at the database data:
Here we see that there are two offices and one home, so which office should be used?
Don't worry, we will know after running the test code:
TUser user = (TUser)session.load(TUser.class,new Integer(4)); System.out.println(user.getAddresses().get("home")); System.out.println(user.getAddresses().get("office"));ShanWei ShangHai
Yes, as the result shows, we get the one behind, which is the same as the principle of Map. The stored values will overwrite the previous values (if they are the same key).
Map is relatively simple, which is comparable to the first two.
4) Let’s take a look at the last one. List is different from the previous ones, and it can be sorted.
Let's take a look at how it is implemented:
First, let’s modify the mapping file:
<list name="addresses" table="t_address" lazy="true"> <key column="user_id" /> <index type="string" column="idx" /> <element type="string" column="address" /> </list>
It is similar to the configuration of Map, but the attributes of index are different. The index in the Map is used as a key to obtain the value, while the index of List is used as sorting.
Let's look at the database:
We set three values in the order of 0, 1, and 2.
Let's run the code to change the values of 0 and 2:
TUser user = (TUser)session.load(TUser.class,new Integer(4)); Transaction tx = session.beginTransaction(); Object obj1 = user.getAddresses().get(0); Object obj2 = user.getAddresses().get(2); user.getAddresses().set(0,obj2); user.getAddresses().set(2,obj1); tx.commit();
We see the results:
We see that 0 and 2 have been replaced, and of course this is just changing the value of idx. But this has basically implemented the sorting function.