Hibernate三个状态以及SaveOrUpdata的使用

参考:https://blog.youkuaiyun.com/daryl715/article/details/1756325

https://www.cnblogs.com/xiaoluo501395377/p/3380270.html

一、hibernate有哪三个对象

  1. 瞬时 (Transient )/临时状态/自由状态:

条件:1.不处于 Session 的缓存中,也可以说,不被任何一个 Session 实例关联。

            2.在数据库中没有对应的记录。

  1. 持久 (Persistent)

条件:1.处于Session的缓存中

      2.在数据库中有对应的记录

  1. 脱管 (Detached)

条件:1.不处于Session的缓存中

             2.数据库中有对应的记录

二、应用

  1. 当瞬时状态转为持久化状态的时候(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 (?, ?, ?)
    

     

  2. 当对象处于持久化状态的时候,如果将对象的非主键属性进行修改,然后提交事务,那么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=?
    

     

  3. 当对象是持久化对象的时候,对对象进行多次的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=?
    

     

  4. 当session调用loadget方法时,此时如果数据库中有该对象,则该对象也变成了一个持久化对象,被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=?
    

     

  5. 游离状态的对象,如果调用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给被操作的对象

四、可能遇到错误

  1. 如果试图修改一个持久化对象的主键的值(该主键有自动生成策略,比如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
    

     

  2. 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]
    

    五、事务相关知识

  1. Hibernate事务存在是为了在遇到执行错误的时候可以进行回滚(系统突然崩或者其他情况),确保了事务的安全。.
  2. 在一个session中,持久化对象的变化,不需要调用update等显式语句,由flush方法就可以实现数据库表的更新。
  3. 对于集成了hibernateSpring的框架,大多数情况下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

https://www.cnblogs.com/xiaoluo501395377/p/3380270.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值