Hibernate 学习之路(二):Hibernate中的Session详解和持久化对象(二)

关于持久化对象经过 Session 处理后的流程图

mark

进一步测试session的方法
class TestHibernate2 {

    private SessionFactory sessionFactory = null;
    private Session session = null;
    private Transaction transaction = null;


    /**
     * init 和 destroy 方法略
     */

    /*
     * 通过调用原生的Connection对象来调用数据库的存储过程
     *   Work 接口: 直接通过 JDBC API 来访问数据库的操作
     * Session 的 doWork(Work) 方法用于执行 Work 对象指定的操作, 
     * 即调用 Work 对象的 execute() 方法.
     * Session 会把当前使用的数据库连接传递给 execute() 方法.
     *
     */
    @Test
    void testDoWork() {
        this.session.doWork(new Work() {

            @Override
            public void execute(Connection arg0) throws SQLException {
                System.out.println(arg0);   //com.mysql.jdbc.JDBC4Connection@33a053d
                //调用存储过程(同jdbc方式调用存储过程)
                //还可以进行批量操作(使用原生的connection)
            }
        });
    }

    /*
     * evict:从session缓存中把指定的持久化对象移除
     */
    @Test
    void testEvict() {
        News news = this.session.get(News.class, 3);
        News news2 = this.session.get(News.class, 4);

        news.setAuthor("123");
        news2.setAuthor("123");
        //调用evict方法后无法发送update语句进行更新news对象对应的数据
        this.session.evict(news);   
    }

    /*
     * delete:执行删除操作,
     * 只要OID和数据表中的一条记录对应(无论是游离对象还是持久化对象)
     * 就会准备执行delete操作,将数据库中对应的记录删除掉
     * 如果OID在数据表中没有对应的记录,那么抛出异常
     * 注意:
     * 可以通过设置hibernate 配置文件的hibernate.use_identifier_rollback为true,
     * 使得被删除的对象的OID设置为null
     */
    @Test
    void testDelete() {
        News news = this.session.get(News.class, 2);
        //准备执行删除数据表记录操作,等到事务提交的时候才进行真正的删除操作
        this.session.delete(news);  
        //news对象依然存在,并且这条语句优先delete语句执行 
        System.out.println(news);   

    }


    /*
     * 注意:
     * 1. 若OID不为空,但是数据表中没有这个OID对应的记录,那么会抛出异常
     * 2. 若OID为null,本来应该是游离对象,
     * 但是如果OID等于id的unsaved-value属性值的对象,也会被认为是游离对象(了解)
     */
    @Test
    void testSaveOrUpdate() {
        News news = new News("qq", "jj", new Date());
//      news.setId(111);
        this.session.saveOrUpdate(news);
    }


    /*
     * update:
     * 1. 若更新一个持久化对象,不需要显示的调用session的update方法,
     * 因为在调用Transaction的commit方法之前就已经执行了
     *
     * 2. 若更新一个游离的对象,就需要显示的调用session的update方法,
     * 可以把一个游离的对象变为持久化对象
     * 
     * 注意:
     * 1. 无论要更新的游离状态的对象和数据表的记录是否一致,
     * 都会发送update语句(因为新开的session并不能确定游离状态的对象和
     *  数据库中的对应的记录是否一致
     *  那么如何才能让session的update方法不盲目的发送update语句呢?
     * 在.hbm.xml文件的class节点设置select-before-update=true
     *  (默认为false),但是通常不要设置这个属性,因为效率较低,
     * 一般都是使用hibernate的触发器才可能使用这个属性
     * 2. 若数据表中没有对应的记录,但还是调用了update方法,那么会抛出异常
     * 
     * 3. 当update方法关联一个游离的对象时,如果在session的缓存中已经有了相同OID的对象,
     * 那么调用update方法将会抛出异常,
     * 因为在session的缓存中是不允许有两个OID相同的对象!
     */
    @Test
    void testUpdate() {
        News news = this.session.get(News.class, 1);
        this.transaction.commit();
        this.session.close();

//      news.setId(1000);

        //之前的session已经被关闭,存储在session缓存中的对象成为了游离状态,
        //此时新开的session缓存中没有之前查询的这个处于
        //游离状态的对象,如果想要通过新开的session对象将游离状态的对象持久化,
        //就需要手工调用session的update方法了
        this.session = this.sessionFactory.openSession();
        this.transaction = this.session.beginTransaction();
        News news2 = this.session.get(News.class, 1);
        //news.setAuthor("老二爹");
        this.session.update(news);
    }


    /*
     * get vs load
     * 
     * 1. 执行get方法会立即加载对象(立即检索)
     *      执行load方法,若不使用该对象,则不会立即执行查询操作,
     * 而是返回一个代理对象(延迟加载) 
     * 
     * 2. load方法可能会抛出懒加载异常 lazyInitiallizationException 异常:
     * 需要初始化代理对象之前已经关闭了session
     * 
     * 3. 若数据表中没有对应的记录,并且session没有关闭的情况下
     * get返回null
     * load方法如果不使用对象的任何属性,则没有问题;
     * 若需要初始化代理对象时,就抛出异常
     */
    @Test
    void testLoad() {
        News news = this.session.load(News.class, 10);
        System.out.println(news.getClass().getName());
//      this.session.close();
        System.out.println(news);
    }

    @Test
    void testGet() {
        News news = this.session.get(News.class, 10);
//      this.session.close();
        System.out.println(news);
    }


    /*
     * persist方法:也可以保存数据,执行insert语句
     * 
     * 和save方法的区别:
     * 在调用persist方法之前,若对象已经有id主键了,
     * 将抛出异常并不会执行insert语句
     */
    @Test
    void testPersist() {
        News news = new News("cc", "张三", new Date());
        System.out.println(news);
//      news.setId(1001);
        this.session.persist(news);;
    }


    /*
     * save方法详解:
     * 1. 使一个临时对象变为持久化对象
     * 2. 为对象分配id(使用数据库自己的主键生成策略)
     * 3. 在flush缓存时会发送一条insert语句
     * 4. 在save方法之前设置的id可以设置,但是无效
     * 5. 持久化后的对象的id是不可以改变的!
     */
    @Test
    public void testSave() {

        News news = new News("aa", "张三", new Date());
        System.out.println(news);
        news.setId(1001);
        this.session.save(news);
        news.setId(222);
        System.out.println(news);
    }
}
拓展:Hibernate 与触发器协同工作
  • Hibernate 与数据库中的触发器协同工作时, 会造成两类问题
    1. 触发器使 Session 的缓存中的持久化对象与数据库中对应的数据不一致:触发器运行在数据库中, 它执行的操作对 Session 是透明的
    2. Session 的 update() 方法盲目地激发触发器: 无论游离对象的属性是否发生变化, 都会执行 update 语句, 而 update 语句会激发数据库中相应的触发器
  • 解决方案:
    1.在执行完 Session 的相关操作后, 立即调用 Session 的 flush() 和 refresh() 方法, 迫使 Session 的缓存与数据库同步(refresh() 方法重新从数据库中加载对象)

    mark

    2.在映射文件的的 元素中设置 select-before-update 属性: 当 Session 的 update 或 saveOrUpdate() 方法更新一个游离对象时, 会先执行 Select 语句, 获得当前游离对象在数据库中的最新数据, 只有在不一致的情况下才会执行 update 语句,当然带来的问题就是影响数据库的效率
    获取源代码请点击我

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值