参考:https://blog.youkuaiyun.com/daryl715/article/details/1756325
https://www.cnblogs.com/xiaoluo501395377/p/3380270.html
一、hibernate有哪三个对象
- 瞬时 (Transient )/临时状态/自由状态:
条件:1.不处于 Session 的缓存中,也可以说,不被任何一个 Session 实例关联。
2.在数据库中没有对应的记录。
- 持久 (Persistent)
条件:1.处于Session的缓存中
2.在数据库中有对应的记录
- 脱管 (Detached)
条件:1.不处于Session的缓存中
2.数据库中有对应的记录
二、应用
- 当瞬时状态转为持久化状态的时候(save),hibernate会发出一条insert语句 -----表示数据库中原来并没有这个对
session = HibernateUtil.openSession(); session.beginTransaction(); User user = new User(); user.setUsername("aaa"); user.setPassword("aaa"); user.setBorn(new Date()); /* * 以上user就是一个Transient(瞬时状态),此时user并没有被session进行托管,即在session的 * 缓存中还不存在user这个对象,当执行完save方法后,此时user被session托管,并且数据库中存在了该对象 * user就变成了一个Persistent(持久化对象) */ session.save(user); session.getTransaction().commit(); --------------------------------------------------------- Hibernate: insert into t_user (born, password, username) values (?, ?, ?)
- 当对象处于持久化状态的时候,如果将对象的非主键属性进行修改,然后提交事务,那么hibernate出来会发送insert语句还有update语句
session = HibernateUtil.openSession(); session.beginTransaction(); User user = new User(); user.setUsername("aaa"); user.setPassword("aaa"); user.setBorn(new Date()); //以上u就是Transient(瞬时状态),表示没有被session管理并且数据库中没有 //执行save之后,被session所管理,而且,数据库中已经存在,此时就是Persistent状态 session.save(user); //此时u是持久化状态,已经被session所管理,当在提交时,会把session中的对象和目前的对象进行比较 //如果两个对象中的值不一致就会继续发出相应的sql语句 user.setPassword("bbb"); //此时会发出2条sql,一条用户做插入,一条用来做更新 session.getTransaction().commit(); ---------------------------------------------------------- Hibernate: insert into t_user (born, password, username) values (?, ?, ?) Hibernate: update t_user set born=?, password=?, username=? where id=?
- 当对象是持久化对象的时候,对对象进行多次的save或者update等方法,hibernate暂时不发送sql语句。只有当事务提交的时候,才将session中的对象(也可认为是多次操作后的对象)和数据库中的进行对比,发出insert或者update语句。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); session = HibernateUtil.openSession(); session.beginTransaction(); User u = new User(); u.setBorn(new Date()); u.setUsername("zhangsan"); u.setPassword("zhangsan"); session.save(u); u.setPassword("222"); //该条语句没有意义 session.save(u); u.setPassword("zhangsan111"); //没有意义 session.update(u); u.setBorn(sdf.parse("1988-12-22")); //没有意义 session.update(u); session.getTransaction().commit(); ---------------------------------------------------------------------------- Hibernate: insert into t_user (born, password, username) values (?, ?, ?) Hibernate: update t_user set born=?, password=?, username=? where id=?
- 当session调用load、get方法时,此时如果数据库中有该对象,则该对象也变成了一个持久化对象,被session所托管(如果在提交事务前,使用了session.clear,继续事务提交的时候,hibernate不会发出update语句)
session = HibernateUtil.openSession(); session.beginTransaction(); //此时u是Persistent User u = (User)session.load(User.class, 4); //由于u这个对象和session中的对象不一致,所以会发出sql完成更新 u.setUsername("bbb"); session.getTransaction().commit(); ------------------------------------------------------------------------------------- Hibernate: select user0_.id as id0_0_, user0_.born as born0_0_, user0_.password as password0_0_, user0_.username as username0_0_ from t_user user0_ where user0_.id=? Hibernate: update t_user set born=?, password=?, username=? where id=?
- 游离状态的对象,如果调用save方法,如果主键设置为自生成策略,那么hibernate会发送一个新 的insert语句。如果要将游离状态的对象转化为持久化对象,那么使用update方法,这时hibernate才会发送update语句。(不太确定),因此为了用错save或者是update方法,一般使用saveOrupdate()方法
session = HibernateUtil.openSession(); session.beginTransaction(); //此时u是一个离线对象,没有被session托管 User u = new User(); u.setId(4); (数据库中已经存在id为4的对象,此时为游离对象) u.setPassword("hahahaha"); //当执行save的时候总是会添加一条数据,此时id就会根据Hibernate所定义的规则来生成 session.save(u); session.getTransaction().commit(); ----------------------------------------------------------------- Hibernate: insert into t_user (born, password, username) values (?, ?, ?)
三、SaveOrUpdate和unsaved-value的关系(重点理解)
unsaved-value可以是下列几个选项:
1)null:主键是对象类型(String/Integer/Long),Hibernate判断操作对象的主键是否为null,来判断操作对象是否以被持久化,如果是,调用save方法,生成insert语句,在数据库中增加一条记录,如果不是,设置主键则直接生成update的SQL语句,发送update,如果数据库中没有那条记录则抛出异常。
2)none:由于不论主键属性为任何值,都不可能为none,因此Hibernate总是对被操作对象发送update。
3)any:由于不论主键属性为任何值,都肯定为any,因此Hibernate总是对被操作对象发送save,Hibernate生成主键。
当数据库进行save操作以后,数据库会返回一个id给被操作的对象
四、可能遇到错误
- 如果试图修改一个持久化对象的主键的值(该主键有自动生成策略,比如ID)的话,就会抛出异常(非主键不会报错)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); session = HibernateUtil.openSession(); session.beginTransaction(); User u = new User(); u.setId(5); //完成update之后也会变成持久化状态 session.update(u); u.setBorn(sdf.parse("1998-12-22")); u.setPassword("lisi"); u.setUsername("lisi"); //会抛出异常 u.setId(333); session.getTransaction().commit(); -------------------------------------------------------------------------------- org.hibernate.HibernateException: identifier of an instance of com.xiaoluo.bean.User was altered from 5 to 333
- session中保存两个相同的持久化对象,提交事务的时候报错(用merge解决)
session = HibernateUtil.openSession(); session.beginTransaction(); //u1已经是持久化状态 User u1 = (User)session.load(User.class, 3); System.out.println(u1.getUsername()); //u2是离线状态 User u2 = new User(); u2.setId(3); u2.setPassword("123456789"); //此时u2将会变成持 久化状态,在session的缓存中就存在了两份同样的对象,在session中不能存在两份拷贝,否则会抛出异常 ---------------------------------------------------------------------------- session.saveOrUpdate(u2); org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.xiaoluo.bean.User#3]
五、事务相关知识
- Hibernate事务存在是为了在遇到执行错误的时候可以进行回滚(系统突然崩或者其他情况),确保了事务的安全。.
- 在一个session中,持久化对象的变化,不需要调用update等显式语句,由flush方法就可以实现数据库表的更新。
- 对于集成了hibernate和Spring的框架,大多数情况下Spring都将hibernate的事务设置为自动提交,一般一个service方法调用一个session
<property name="hibernate.connection.autocommit">true</property> |
4.flush方法的主要作用就是清理缓存,强制数据库与Hibernate缓存同步,以保证数据的一致性。它的主要动作就是向数据 库发送一系列的sql语句,并执行这些sql语句,但是不会向数据库提交。而commit方法则会首先调用flush方法,然后提交事务。
六、扩展
(问题1,级联保存的时候) Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
(级联的时候,子对象并没有明显是使用save或者是update方法,父对象有,所以子对象要配置)
不是网上各种说法,而是当选择 assigned 生成器时,除非有一个 version 或 timestamp 属性,或者你定义了 Interceptor.isUnsaved(),否则需要让 Hiberante 使用 unsaved-value="undefined",强制Hibernatet 查询数据库来确定一个实例是瞬时的(transient) 还是脱管的(detached)。
https://blog.youkuaiyun.com/tianlincao/article/details/6040118
(问题2)当我们只更新部分字段的时候,如果使用update语句的话,由于hibernate发送的update语句更新的是全部属性字段,如果此时session中没有缓存其他字段,那么其他字段将会使用默认值更新到数据库中,因此会造成原有数据的丢失,于是我们要在配置文件中,将dynamic-update(动态更新)配置为true(默认为false)。配置以后,调用update语句以后,只更新修改部分,其他属性保持原样
参考:https://blog.youkuaiyun.com/daryl715/article/details/1756325