原创地址:http://www.cnblogs.com/binyulan/p/5628579.html
Session是java应用程序和hibernate框架之间的一个主要接口。它是从持久化服务中剥离出来的一个非常重要的API接口。
Session的主要功能是为映射的实体类的实例提供增删改查操作(User.class 为被映射的实体类,new User()即为实例)。这些实例可能是以下三种状态之一:
1) transient: 从没有被持久化,不在Session缓存中
2) persistent: 在Session的缓存中。
3) detached: 曾经是persistent状态,现在不在Session缓存中。
transient 实例通过调用save(), persist() or saveOrUpdate()可以变成persistent实例。persistent实例通过调用delete可以变成transient实例。任何通过get()或者load()方法获取的实例都是persistent实例。detached实例通过调用update(), saveOrUpdate(), lock() or replicate()可以变成persistent实例。transient或者detached实例通过调用merge()方法可以生成一个新的persistent实例,但是并不是他们自己,这个persistent实例是merge方法自己创建的。
save() 和 persist() 生成SQL insert。delete() 生成SQL delete。update() 和 merge()生成SQL update。对persistent实例的修改在flush时会生成SQL update。saveOrUpdate() 和 replicate()生成insert或者update。
Session接口的实现类并没有专门设计为线程安全的。相反,每个线程或者事务都应该通过SessionFactory获取自己的实例。
一个典型的事务应该使用如下的形式:
Session sess = factory.openSession(); Transaction tx; try { tx = sess.beginTransaction(); //do some work ... tx.commit(); } catch (Exception e) { if (tx!=null) tx.rollback(); throw e; } finally { sess.close(); }
如果Session抛出异常,事务必须回滚。同时Session被弃用,因为Session内部状态和数据库可能会不一致。
1 保存对象到数据库 session.save(Object user); //参数user为transient实例,返回值为新生成的主键id
持久化transient实例。返回值为生成的主键id。
按照第一节的配置,主键的生成策略为native(数据库自动生成主键),由于数据库使用的是mysql 5,所以是自增的主键生成方式。保存对象时并不需要设置id属性。
@Test public void testSave() { try { User user = new User(); user.setBirthday(new java.sql.Date(0)); user.setName("binyulan"); session.save(user); } catch (HibernateException e) { if (transaction != null) { transaction.rollback(); } throw e; } }
2 保存对象到数据库 session.persist(Object user); //参数user为transient实例
持久化transient实例。这个方法和session.save()方法一样,都是保存对象到数据库。但是不能设置id属性,否则抛出异常。
@Test public void testPersist() { try { User user = new User(); user.setBirthday(new java.sql.Date(0)); user.setName("binyulan"); user.setId(13); session.persist(user); } catch (HibernateException e) { e.printStackTrace(); if (transaction != null) { transaction.rollback(); } throw e; } }
执行testPersist后抛出异常,如下:
org.hibernate.PersistentObjectException: detached entity passed to persist: com.binyulan.domain.User
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
user创建出来为transient实例,但是异常显示是detached实例。所以我认为只要给对象设置了id属性,即使没有持久化过,也可以看做detached实例。
persist()方法的参数为transient实例,即不带主键id的实例。save()方法参数也为transient实例,但是也可以给此实例设置id属性,即detached实例,
只不过此id属性不起作用(native生成方式下)。结论如下:
save()和persist()方法参数虽然都说明为transient实例,但是也可以传detached实例,也可以传persistent实例。但是没有理由传非transient实例,因为这些方法都是设计为传transient实例的,虽然传其他实例也可以,但是不推荐,谁知道100年后hibernate90.10.9会不会改变save()或persist()方法传入非transient实例的行为呢。
3 合并对象到数据库 session.merge(Object user); //参数user为detached实例,其属性将被copy到persistent实例。并且返回此persistent实例
拷贝user到持久化对象。如果没有persistent对象在session缓存中,则加载数据到persistent对象并且返回此persistent对象。
如果user在数据库中没有对应记录,则拷贝user到一个副本作为persistent对象并且返回这个persistent对象。user不会被保存到session缓存中
1) 保存未设置id属性的User对象,执行insert把User对象保存到数据库中,同时返回保存后的User对象。
@Test public void testMerge() { try { User user = new User(); user.setBirthday(new java.sql.Date(0)); user.setName("binyulan2"); User user1 = (User) session.merge(user); //把user属性拷贝到新建的user1对象,执行insert插入数据库,并且返回新建的user1对象,user1包含id,在session缓存中 System.out.println(user1 == user); //返回false。user为新建的对象,并没有放入session缓存。user1为新建的持久化对象,并且在session的缓存中。 } catch (HibernateException e) { e.printStackTrace(); if (transaction != null) { transaction.rollback(); } throw e; } }
2) 保存设置id属性的User对象,首先执行select查询数据库。若数据库中存在此id的记录,并且与数据库中记录不一致,则执行update操作,
若不存在此记录,否则执行insert操作。
@Test public void testMerge1() { try { User user = new User(); user.setBirthday(new java.sql.Date(0)); user.setName("binyulan123"); user.setId(20); /** * 把user属性拷贝到新建的User对象,并且执行select查询数据库。 * 假设数据库中存在id为20的记录,且user对象与数据库中记录不一致,执行update */ User user1 = (User) session.merge(user); System.out.println(user1 == user); //返回false。user为参数,不在session缓存中,user1为新建的持久化对象,并且在session的缓存中。 /** * 把user属性拷贝到新建的User对象,并且执行select查询数据库。 * 假设数据库中不存在id为123456789的记录,执行insert */ user.setId(123456789L); User user2 = (User) session.merge(user); System.out.println(user2 == user); //返回false。user为参数,不再session缓存中,user2为新建的持久化对象,并且在session的缓存中。 } catch (HibernateException e) { e.printStackTrace(); if (transaction != null) { transaction.rollback(); } throw e; } }
4 数据更新,session.update(Object user);// 参数为一个detached实例。
使用user更新数据库中记录,并且把user对象变为persistent实例。
@Test public void testUpdate() { User user = new User(); user.setName(null); user.setBirthday(null); user.setId(20); session.update(user); }
5 更新或者保存,session.saveOrUpdate(Object user);
@Test public void testSaveOrUpdate() { User user = new User(); user.setName(null); user.setBirthday(null); session.saveOrUpdate(user); //insert操作 User user1 = new User(); user1.setName(null); user1.setBirthday(null); user1.setId(9); session.saveOrUpdate(user1);//update操作 }
6 删除操作,session.delete(Object user);
从session缓存中删除一个persistent实例。参数为一个Session缓存中的实例或者一个带id的transient实例
@Test public void testDelete() { User user = new User(); user.setId(9); session.delete(user); }
7 查询操作,session.get(Class clazz, Serializable id);
根据给定的Class对象和主键id返回persistent实例,如果数据库中没有此记录,则返回null。
@Test public void testGet() { User user = (User) session.get(User.class, 1L); System.out.println(user); }
8 查询操作,session.load(Class theClass, Serializable id);
返回一个代理对象,并在访问非主键id属性时会初始化这个代理对象。不能使用这个方法判断是否一个实例存在。即使数据库中没有相应记录,也会返回代理对象,但是在使用此代理对象是就会报错。
@Test public void testLoad() { User user = (User) session.load(User.class, 1L); //返回的user为代理对象 user.getName(); //访问name属性时向数据库发送SQL select System.out.println(user.getClass()); //代理对象类 }
9 复制操作,replicate(Object object, ReplicationMode replicationMode);
@Test public void testReplicate() { User user = new User(); user.setId(48);// 假定数据库中存在id为48的记录 user.setBirthday(new java.sql.Date(0)); user.setName("binyulan1"); /** * 可以选择复制的模式,OVERWRITE模式为如果存在此记录,则覆盖 * 执行SQL select */ session.replicate(user, ReplicationMode.OVERWRITE); session.flush();// 执行SQL update User user1 = new User(); user1.setId(48); user1.setBirthday(new java.sql.Date(0)); user1.setName("binyulan2"); /** * 可以选择复制的模式,IGNORE模式为如果存在此记录,则不进行更新 * 执行SQL select */ session.replicate(user1, ReplicationMode.IGNORE); session.flush();// 记录存在,所以忽略掉,不会执行SQL update User user3 = new User(); user3.setId(48); user3.setBirthday(new java.sql.Date(0)); user3.setName("binyulan3"); /** * 可以选择复制的模式,EXCEPTION模式为如果存在此记录,则抛异常 * 此语句执行SQL insert,ReplicationMode.EXCEPTION 始终都会执行SQL insert。 * 使用数据库自身约束抛出异常,因为使用的是native主键生成方式,所以此处不抛异常,会插入成功 */ session.replicate(user3, ReplicationMode.EXCEPTION); }
10 强制同步数据到数据库,session.flush();
可以设置FlushMode, 如果设置为NERVER,且不手动执行session.flush(), 提交事务也不会执行SQL update。
System.out.println(session.getFlushMode()); //默认模式为AUTO
@Test public void testFlush() { User user = new User(); user.setId(1); //假设id为1的记录存在 user.setBirthday(new java.sql.Date(0)); user.setName("binyulan3"); session.update(user); session.flush(); //强制执行SQL update }
11 同步数据库中数据到java对象,session.refresh(Object user); //参数为persistent实例或者detached实例
重新读取数据库记录到user,不建议在跨多个业务长时间运行的session中使用。在以下特定场景中使用
1) 触发器出发了一个insert或者update
2) 执行一大堆sql后
3) 插入一个Blob或Clob后
@Test public void testRefresh() { User user = (User) session.get(User.class, 1L); user.setName("original"); session.refresh(user); //重新同步数据库中记录到user,此时user的name不是original System.out.println(user.getName()); // 输出binyulan }
请勿转载,谢谢合作