笔记46 Hibernate快速入门(三)

Hibernate相关概念

一、事物概念

Hibernate的任何对数据有改动的操作,都应该被放在事务里面。

hibernate中的事务由s.beginTransaction();开始由s.getTransaction().commit();结束

本例子,执行了两个操作

第一个,删除id=1的产品,这个是会成功的
第二个,修改id=2的产品,使得其产品名称超过了数据库中设置的长度30,这个是会失败的。
因为这两个操作都是在一个事务中,而且第二个操作失败了,所以最后的结果是两个操作都没有生效

 1 package hibernate.test;
 2 
 3 import org.hibernate.Session;
 4 import org.hibernate.SessionFactory;
 5 import org.hibernate.cfg.Configuration;
 6 
 7 import hibernate.pojo.Product;
 8 
 9 public class testTransaction {
10 
11     public static void main(String[] args) {
12         // TODO Auto-generated method stub
13 
14         SessionFactory sFactory = new Configuration().configure().buildSessionFactory();
15         Session session = sFactory.openSession();
16         session.beginTransaction();
17 
18         Product product = (Product) session.get(Product.class, 1);
19         session.delete(product);
20 
21         Product product2 = (Product) session.get(Product.class, 2);
22         product2.setName("iphoneiphoneiphoneiphoneiphoneiphoneiphoneiphoneiphone");
23         session.update(product2);
24 
25         session.getTransaction().commit();
26         session.close();
27         sFactory.close();
28     }
29 
30 }

二、属性延迟加载

属性的延迟加载: 当使用load的方式来获取对象的时候,只有访问了这个对象的属性,hibernate才会到数据库中进行查询,否则不会访问数据库。

 1 package hibernate.test;
 2 
 3 import org.hibernate.Session;
 4 import org.hibernate.SessionFactory;
 5 import org.hibernate.cfg.Configuration;
 6 
 7 import hibernate.pojo.Product;
 8 
 9 public class testDelayProperty {
10 
11     public static void main(String[] args) {
12         // TODO Auto-generated method stub
13 
14         SessionFactory sFactory = new Configuration().configure().buildSessionFactory();
15         Session session = sFactory.openSession();
16         session.beginTransaction();
17 
18         Product product = (Product) session.load(Product.class, 1);
19         System.out.println("log1");
20         System.out.println(product.getName());
21         System.out.println("log2");
22 
23         session.getTransaction().commit();
24         session.close();
25         sFactory.close();
26     }
27 
28 }

三、关系延迟加载

延迟加载又叫lazyload,在one-many many-many的时候都可以使用关系的延迟加载。

<set name="products" lazy="false">改为<set name="products" lazy="true">表示通过Category获取产品是延迟加载的。

 1 <?xml version="1.0"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC
 3         "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 4         "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 5  
 6 <hibernate-mapping package="hibernate.pojo">
 7     <class name="Category" table="category">
 8         <id name="id" column="id">
 9             <generator class="native">
10             </generator>
11         </id>
12         <property name="name" />
13  
14         <set name="products" lazy="true">
15             <key column="cid" not-null="false" />
16             <one-to-many class="Product" />
17         </set>            
18     </class>
19 </hibernate-mapping>

测试:

  执行22行的时候,只会查询Category表的信息,不会查询product表。只有在执行24行,通过category取products的时候,才会进行对product表的查询。

 1 package hibernate.test;
 2 
 3 import java.util.Iterator;
 4 import java.util.Set;
 5 
 6 import org.hibernate.Session;
 7 import org.hibernate.SessionFactory;
 8 import org.hibernate.cfg.Configuration;
 9 
10 import hibernate.pojo.Category;
11 import hibernate.pojo.Product;
12 
13 public class testDelayRelation {
14 
15     public static void main(String[] args) {
16         // TODO Auto-generated method stub
17 
18         SessionFactory sFactory = new Configuration().configure().buildSessionFactory();
19         Session session = sFactory.openSession();
20         session.beginTransaction();
21 
22         Category category = (Category) session.get(Category.class, 4);
23         System.out.println("log1");
24         Set<Product> products = category.getProducts();
25         Iterator<Product> iterator = products.iterator();
26         while (iterator.hasNext()) {
27             System.out.println(iterator.next().getName());
28 
29         }
30         System.out.println("log2");
31 
32         session.getTransaction().commit();
33         session.close();
34         sFactory.close();
35     }
36 
37 }

四、级联

  什么是级联? 简单的说,就是没有配置级联的时候,删除分类,其对应的产品不会被删除。 但是如果配置了恰当的级联,那么删除分类的时候,其对应的产品都会被删除掉。

  级联有4种类型:

all:所有操作都执行级联操作;

none:所有操作都不执行级联操作;

delete:删除时执行级联操作; 

save-update:保存和更新时执行级联操作;

级联通常用在one-many和many-to-many上,几乎不用在many-one上。

1.delete级联

在Category.hbm.xml 加上 <set name="products" cascade="delete" lazy="false">

在删除分类的时候,会把分类下对应的产品都删除掉,否则只会把产品对应的cid设置为空。

2.save-update级联

  运行代码就会发现,这3个瞬时状态的产品对象虽然没有添加到数据库里,但是在事务提交的时候,会把他们3个持久化。 如果没有cascade="save-update",就会报错。

 1 package hibernate.test;
 2 
 3 import org.hibernate.Session;
 4 import org.hibernate.SessionFactory;
 5 import org.hibernate.cfg.Configuration;
 6 
 7 import hibernate.pojo.Category;
 8 import hibernate.pojo.Product;
 9 
10 public class testCascadeSaveUpdate {
11     /*
12      * 级联有4种类型: all:所有操作都执行级联操作; none:所有操作都不执行级联操作; delete:删除时执行级联操作;
13      * save-update:保存和更新时执行级联操作;
14      */
15     public static void main(String[] args) {
16         // TODO Auto-generated method stub
17 
18         SessionFactory sFactory = new Configuration().configure().buildSessionFactory();
19         Session session = sFactory.openSession();
20         session.beginTransaction();
21 
22         Category category = (Category) session.get(Category.class, 5);
23         Product product = new Product();
24         product.setName("xiaomi_note");
25         Product product2 = new Product();
26         product2.setName("xiaomi_max");
27         Product product3 = new Product();
28         product3.setName("xiaomi_8");
29 
30         category.getProducts().add(product);
31         category.getProducts().add(product2);
32         category.getProducts().add(product3);
33 
34         session.getTransaction().commit();
35         session.close();
36         sFactory.close();
37     }
38 
39 }

五、一级缓存

  hibernate默认是开启一级缓存的,一级缓存存放在session上。

例子:

第一次通过id=1获取对象的时候,session中是没有对应缓存对象的,所以会在"log1"后出现sql查询语句。
第二次通过id=1获取对象的时候,session中有对应的缓存对象,所以在"log2"后不会出现sql查询语句。

 1 package hibernate.test;
 2 
 3 import org.hibernate.Session;
 4 import org.hibernate.SessionFactory;
 5 import org.hibernate.cfg.Configuration;
 6 
 7 import hibernate.pojo.Category;
 8 
 9 public class testCacheL1 {
10 
11     public static void main(String[] args) {
12         // TODO Auto-generated method stub
13 
14         SessionFactory sFactory = new Configuration().configure().buildSessionFactory();
15         Session session = sFactory.openSession();
16         session.beginTransaction();
17 
18         System.out.println("log1");
19         Category category = (Category) session.get(Category.class, 1);
20         System.out.println("log2");
21         Category category2 = (Category) session.get(Category.class, 1);
22         System.out.println("log3");
23 
24         session.getTransaction().commit();
25         session.close();
26         sFactory.close();
27     }
28 
29 }

六、二级缓存

  Hibernate的一级缓存是在Session上,二级缓存是在SessionFactory上。

创建了两个Session:

在第一个Session里

第一次获取id=1的Category 会执行SQL语句

第二次获取id=1的Category,不会执行SQL语句,因为有一级缓存

在第二个Session里

获取id=1的Category,如果没有设置二级缓存时,会执行SQL语句,因为在第二个Session,没有缓存该对象。 所以总共会看到两条SQL语句。

如果设置了二级缓存就使用不同的session,都去获取id=1的category,只会访问一次数据库。因为第二次获取虽然没有从第二个session中拿到缓存,但是从sessionfactory中拿到了Category缓存对象。

1.在hibernate.cfg.xml 中增加对二级缓存的配置

 1 <?xml version='1.0' encoding='utf-8'?>
 2 <!DOCTYPE hibernate-configuration PUBLIC
 3        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
 4 "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
 5 
 6 <hibernate-configuration>
 7 
 8     <session-factory>
 9         <!-- Database connection settings -->
10         <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
11         <property name="connection.url">jdbc:mysql://localhost:3306/tests?characterEncoding=UTF-8</property>
12         <property name="connection.username">root</property>
13         <property name="connection.password">123456</property>
14         <!-- SQL dialect -->
15         <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
16         <property name="current_session_context_class">thread</property>
17         <property name="show_sql">true</property>
18         <property name="hbm2ddl.auto">update</property>
19         <property name="hibernate.cache.use_second_level_cache">true</property> 
20             <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> 
21         <mapping resource="hibernate/pojo/Product.hbm.xml" />
22         <mapping resource="hibernate/pojo/Category.hbm.xml" />
23         <mapping resource="hibernate/pojo/User.hbm.xml" />
24     </session-factory>
25 
26 </hibernate-configuration>

2.在src目录下,创建一个ehcache.xml用于EHCache的缓存配置。

 1 <ehcache>
 2     <diskStore path="java.io.tmpdir"/>
 3     <defaultCache
 4         maxElementsInMemory="10000"
 5         eternal="false"
 6         timeToIdleSeconds="120"
 7         timeToLiveSeconds="120"
 8         overflowToDisk="true"
 9         />
10 </ehcache>

3.对于要进行二级缓存的实体类,进行配置,增加<cache usage="read-only" />

4.测试

 1 package hibernate.test;
 2 
 3 import org.hibernate.Session;
 4 import org.hibernate.SessionFactory;
 5 import org.hibernate.cfg.Configuration;
 6 
 7 import hibernate.pojo.Category;
 8 
 9 public class testCacheL2 {
10 
11     public static void main(String[] args) {
12         // TODO Auto-generated method stub
13 
14         SessionFactory sFactory = new Configuration().configure().buildSessionFactory();
15         Session session = sFactory.openSession();
16         session.beginTransaction();
17 
18         Category category = (Category) session.get(Category.class, 1);
19         System.out.println("log1");
20         Category category2 = (Category) session.get(Category.class, 1);
21         System.out.println("log2");
22         session.getTransaction().commit();
23         session.close();
24 
25         Session session2 = sFactory.openSession();
26         session2.beginTransaction();
27         Category category3 = (Category) session2.get(Category.class, 1);
28         System.out.println("log3");
29 
30         session2.getTransaction().commit();
31         session2.close();
32 
33         sFactory.close();
34     }
35 
36 }

七、分页

  使用Criteria进行分页查询Hibernate使用Criteria 来进行分页查询c.setFirstResult(2); 表示从第2条数据开始c.setMaxResults(5); 表示一共查询5条数据

 1 package hibernate.test;
 2 
 3 import java.util.List;
 4 
 5 import org.hibernate.Criteria;
 6 import org.hibernate.Session;
 7 import org.hibernate.SessionFactory;
 8 import org.hibernate.cfg.Configuration;
 9 import org.hibernate.criterion.Restrictions;
10 
11 import hibernate.pojo.Product;
12 
13 public class testPaging {
14 
15     public static void main(String[] args) {
16         // TODO Auto-generated method stub
17 
18         SessionFactory sFactory = new Configuration().configure().buildSessionFactory();
19         Session session = sFactory.openSession();
20         session.beginTransaction();
21 
22         String name = "iphone";
23 
24         Criteria criteria = session.createCriteria(Product.class);
25         criteria.add(Restrictions.like("name", "%" + name + "%"));
26         criteria.setFirstResult(2);
27         criteria.setMaxResults(5);
28 
29         List<Product> products = criteria.list();
30         for (Product p : products) {
31             System.out.println(p.getName());
32         }
33 
34         session.getTransaction().commit();
35         session.close();
36         sFactory.close();
37     }
38 
39 }

八、两种获取方式

Hibernate中根据Id单条查询获取对象的方式有两种,分别是get()和load(),来看一下这两种方式的区别。

1.当get()方法被调用的时候就会立即发出SQL语句。

2.当调用load()方法的时候会返回一个目标对象的代理对象,在这个代理对象中只存储了目标对象的ID值,只有当调用除ID值以外的属性值的时候才会发出SQL查询的。

3.Exception

<1>当使用get获取数据库中不存在的数据时会抛出空指针异常NullPointerException

<2>当使用load获取数据库中不存在的数据时会发生一个ObjectNotFoundException异常。

  这个是因为使用load()的时候返回的是一个代理对象,因为这个时候还没有进行查询,所以我们并没有办法确定要查询的对象到底存在不存在,所以使用load()查询的返回值是永远不会为空的,但是呢,当我们试图访问被代理的真实对象的时候,因为这个对象并不存在,所以就抛出了一个ObjectNotFoundException。

 

  还有一种说法是get()是用于不确定对象是否真的存在的情况,所以在查询出来后可以先进行一个空指针判断,而load()方法用于对象一定存在的情况下,不然等会儿使用的时候就可能会抛出ObjectNotFoundException了。

4.缓存

  get()和load()都会使用缓存,都是首先从一级缓存Session中查找,当找不到的时候再去二级缓存中查找,当查询不到的时候get()返回的是null,而load()则返回代理对象。

  如果使用这两种方式访问同一个id数据,会发现两个返回的都是真实对象,连load()返回的也是真实对象,并且它们引用的还是同一块对象。这个是因为get()查询出来的对象后会将其放入到缓存中,而load()再去查询的时候它会先去缓存中查找,如果缓存中没有的话才会返回代理对象,但是当缓存中已经存在的话就直接将真实对象返回来了。

5.小结

返回值:

get()返回的是查询出来的实体对象,而load()查询出来的是一个目标实体的代理对象。

查询时机:

get()在调用的时候就立即发出SQL语句查询,而load()在访问非ID属性的时候才会发出查询语句并且将被代理对象target填充上,但是如果这个动作发生在Session被关闭后的话就会抛出LazyInitializationException。

查询结果为空时:

get()抛出NullPointerException

load()抛出ObjectNotFoundException

 1 package hibernate.test;
 2 
 3 import org.hibernate.Session;
 4 import org.hibernate.SessionFactory;
 5 import org.hibernate.cfg.Configuration;
 6 
 7 import hibernate.pojo.Product;
 8 
 9 public class testLoadAndGet {
10 
11     /*
12      * load方式是延迟加载,只有属性被访问的时候才会调用sql语句 ;get方式是非延迟加载,无论后面的代码是否会访问到属性,马上执行sql语句
13      */
14     @SuppressWarnings("unused")
15     public static void main(String[] args) {
16         // TODO Auto-generated method stub
17 
18         SessionFactory sFactory = new Configuration().configure().buildSessionFactory();
19         Session session = sFactory.openSession();
20         session.beginTransaction();
21         System.out.println("log1");
22         Product product = (Product) session.get(Product.class, 1);
23         System.out.println("log2");
24         Product product2 = (Product) session.load(Product.class, 2);
25         // Product product2 = (Product) session.load(Product.class, 200);
26         System.out.println("log3");
27         // System.out.println(product2.getName());
28         System.out.println("log4");
29 
30         // Product p3 = (Product) session.get(Product.class, 500);
31         // System.out.println("p3=" + p3.getName());
32 
33         // Product p4 = (Product) session.load(Product.class, 500);
34         // System.out.println("p4=" + p4.getName());
35 
36         Product product3 = (Product) session.load(Product.class, 5);
37         System.out.println(product3.getName());
38         Product product4 = (Product) session.load(Product.class, 5);
39         System.out.println(product4.getName());
40         session.getTransaction().commit();
41         session.close();
42         System.out.println(product.getName());
43         sFactory.close();
44     }
45 
46 }

九、两种Session方式

Hibernate有两种方式获得session,分别是:openSession和getCurrentSession 

区别在于 

1. 获取的是否是同一个session对象 
openSession每次都会得到一个新的Session对象 
getCurrentSession在同一个线程中,每次都是获取相同的Session对象,但是在不同的线程中获取的是不同的Session对象 
2. 事务提交的必要性 
openSession只有在增加,删除,修改的时候需要事务,查询时不需要的 
getCurrentSession是所有操作都必须放在事务中进行,并且提交事务后,session就自动关闭,不能够再进行关闭 

 1 package hibernate.test;
 2 
 3 import org.hibernate.Session;
 4 import org.hibernate.SessionFactory;
 5 import org.hibernate.cfg.Configuration;
 6 
 7 import hibernate.pojo.Product;
 8 
 9 public class testSession {
10 
11     static Session session;
12     static Session session2;
13 
14     public static void main(String[] args) throws InterruptedException {
15         // TODO Auto-generated method stub
16 
17         SessionFactory sFactory = new Configuration().configure().buildSessionFactory();
18         // Session session = sFactory.openSession();
19         // Session session2 = sFactory.openSession();
20 
21         // Session session = sFactory.getCurrentSession();
22         // Session session2 = sFactory.getCurrentSession();
23         //
24         // System.out.println(session == session2);
25 
26         // session.close();
27         // session2.close();
28 
29         Thread thread = new Thread() {
30             public void run() {
31                 session = sFactory.getCurrentSession();
32             }
33         };
34         thread.start();
35 
36         Thread thread2 = new Thread() {
37             public void run() {
38                 session2 = sFactory.getCurrentSession();
39             }
40         };
41         thread2.start();
42 
43         thread.join();
44         thread2.join();
45 
46         System.out.println(session == session2);
47 
48         Session session3 = sFactory.openSession();
49         System.out.println(((Product) session3.get(Product.class, 2)).getName());
50 
51         // Session session4 = sFactory.getCurrentSession();
52         // session4.get(Product.class, 2);
53 
54         session.close();
55         session2.close();
56 
57         Session session5 = sFactory.getCurrentSession();
58         session5.beginTransaction();
59         session5.get(Product.class, 3);
60         session5.getTransaction().commit();
61         // session5.close();
62 
63         sFactory.close();
64     }
65 
66 }

十、N+1

  Hibernate有缓存机制,可以通过用id作为key把product对象保存在缓存中 同时hibernate也提供Query的查询方式。假设数据库中有100条记录,其中有30条记录在缓存中,但是使用Query的list方法,就会所有的100条数据都从数据库中查询,而无视这30条缓存中的记录。 N+1是什么意思呢,首先执行一条sql语句,去查询这100条记录,但是,只返回这100条记录的ID 然后再根据id,进行进一步查询。如果id在缓存中,就从缓存中获取product对象了,否则再从数据库中获取。

  Hibernate使用Iterator实现N+1。

  首先通过Query的iterator把所有满足条件的Product的id查出来然后再通过it.next()查询每一个对象如果这个对象在缓存中,就直接从缓存中取了否则就从数据库中获取N+1中的1,就是指只返回id的SQL语句,N指的是如果在缓存中找不到对应的数据,就到数据库中去查。

 1 package hibernate.test;
 2 
 3 import java.util.Iterator;
 4 
 5 import org.hibernate.Query;
 6 import org.hibernate.Session;
 7 import org.hibernate.SessionFactory;
 8 import org.hibernate.cfg.Configuration;
 9 
10 import hibernate.pojo.Product;
11 
12 public class testN1 {
13 
14     public static void main(String[] args) {
15         // TODO Auto-generated method stub
16 
17         SessionFactory sFactory = new Configuration().configure().buildSessionFactory();
18         Session session = sFactory.openSession();
19         session.beginTransaction();
20 
21         String name = "iphone";
22         Query query = session.createQuery("from Product p where p.name like ?");
23         query.setString(0, "%" + name + "%");
24 
25         Iterator<Product> iterator = query.iterate();
26         while (iterator.hasNext()) {
27             Product product = (Product) iterator.next();
28             System.out.println(product.getName());
29 
30         }
31 
32         Query query2 = session.createQuery("select count(*) from Product p where p.name like ?");
33         query2.setString(0, "%" + name + "%");
34 
35         long total = (Long) query2.uniqueResult();
36         System.out.println(total);
37 
38         session.getTransaction().commit();
39         session.close();
40         sFactory.close();
41     }
42 
43 }

十一、乐观锁

  乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。

1.场景描述:

<1>. 通过session1得到id=1的对象 product1

<2>. 在product1原来价格的基础上增加1000

<3>. 更新product1之前,通过session2得到id=1的对象product2

<4>. 在product2原来价格的基础上增加1000

<5>. 更新product1

<6>. 更新product2

最后结果是product的价格只增加了1000,而不是2000

代码:

 1 package hibernate.test;
 2 
 3 import org.hibernate.Session;
 4 import org.hibernate.SessionFactory;
 5 import org.hibernate.cfg.Configuration;
 6 
 7 import hibernate.pojo.Product;
 8 
 9 public class testOptimisticLock {
10     /*
11      * 场景描述 1. 通过session1得到id=1的对象 product1 2. 在product1原来价格的基础上增加1000
12      * 3.更新product1之前,通过session2得到id=1的对象product2 4. 在product2原来价格的基础上增加1000
13      * 5.更新product1 6. 更新product2
14      */
15 
16     public static void main(String[] args) throws InterruptedException {
17         // TODO Auto-generated method stub
18 
19         SessionFactory sFactory = new Configuration().configure().buildSessionFactory();
20         Session session = sFactory.openSession();
21         Session session2 = sFactory.openSession();
22 
23         session.beginTransaction();
24         session2.beginTransaction();
25 
26         Product product = (Product) session.get(Product.class, 1);
27         System.out.println("产品原价为:" + product.getPrice());
28         product.setPrice(product.getPrice() + 1000);
29 
30         Product product2 = (Product) session2.get(Product.class, 1);
31         product2.setPrice(product2.getPrice() + 1000);
32 
33         session.update(product);
34         session2.update(product2);
35 
36         session.getTransaction().commit();
37         session2.getTransaction().commit();
38 
39         Product product3 = (Product) session.get(Product.class, 1);
40         System.out.println("最终的价格:" + product3.getPrice());
41 
42         session.close();
43         session2.close();
44         sFactory.close();
45     }
46 
47 }

 

2.修改配置文件 Product.hbm.xml

<version name="version" column="ver" type="int"></version>增加一个version字段,用于版本信息控制。这就是乐观锁的核心机制。

比如session1获取product1的时候,version=1。 那么session1更新product1的时候,就需要确保version还是1才可以进行更新,并且更新结束后,把version改为2。
注意: version元素必须紧跟着id后面,否则会出错。

 

 1 <?xml version="1.0"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC
 3         "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 4         "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 5  
 6 <hibernate-mapping package="hibernate.pojo">
 7     <class name="Product" table="product">
 8         <id name="id" column="id">
 9             <generator class="native">
10             </generator>
11         </id>
12         <!--version元素必须紧挨着id后面  -->
13         <version name="version" column="ver" type="int"></version>
14         <property name="name" />
15         <property name="price" />
16         <many-to-one name="category" class="Category" column="cid"></many-to-one>
17         <set name="users" table="user_product" lazy="false" >
18             <key column="pid"></key>
19             <many-to-many column="uid" class="User"></many-to-many>
20         </set>
21     </class>
22      
23 </hibernate-mapping>

3.修改 Product.java

增加version属性

 1 package hibernate.pojo;
 2 
 3 import java.util.Set;
 4 
 5 public class Product {
 6     int id;
 7     String name;
 8     float price;
 9     Category category;
10     Set<User> users;
11     int version;
12     int cid;
13 
14     public int getId() {
15         return id;
16     }
17 
18     public void setId(int id) {
19         this.id = id;
20     }
21 
22     public String getName() {
23         return name;
24     }
25 
26     public void setName(String name) {
27         this.name = name;
28     }
29 
30     public float getPrice() {
31         return price;
32     }
33 
34     public void setPrice(float price) {
35         this.price = price;
36     }
37 
38     public Category getCategory() {
39         return category;
40     }
41 
42     public void setCategory(Category category) {
43         this.category = category;
44     }
45 
46     public Set<User> getUsers() {
47         return users;
48     }
49 
50     public void setUsers(Set<User> users) {
51         this.users = users;
52     }
53 
54     public int getCid() {
55         return cid;
56     }
57 
58     public void setCid(int cid) {
59         this.cid = cid;
60     }
61 
62     public int getVersion() {
63         return version;
64     }
65 
66     public void setVersion(int version) {
67         this.version = version;
68     }
69 
70 }

4.再次运行代码

5.原理

<1>. 假设数据库中产品的价格是10000,version是10
<2>. session1,session2分别获取了该对象
<3>. 都修改了对象的价格
<4>. session1试图保存到数据库,检测version依旧=10,成功保存,并把version修改为11
<5>. session2试图保存到数据库,检测version=11,说明该数据已经被其他人动过了。 保存失败,抛出异常

相关代码:https://github.com/lyj8330328/hibernate.git

posted @ 2018-06-16 16:17  雨落忧伤-  阅读(202)  评论(0编辑  收藏  举报