zoukankan      html  css  js  c++  java
  • (转) Hibernate框架基础——操纵持久化对象的方法(Session中)

    http://blog.csdn.net/yerenyuan_pku/article/details/52761021

    上一篇文章中我们学习了Hibernate中java对象的状态以及对象的状态之间如何转换。本文我们将详细讲解Session中的方法。

    save()

    Session的save()方法使一个临时对象转变为持久化对象。 
    Session的save()方法完成以下操作:

    • 把新new的对象加入到Session缓存中,使它进入持久化状态。
    • 选用映射文件指定的标识符生成器,为持久化对象分配唯一的OID。在使用代理主键的情况下,setId()方法为新new的对象设置OID是无效的。
    • 计划执行一条insert语句,把User对象当前的属性值组装到insert语句中。

    注意:Hibernate通过持久化对象的OID来维持它和数据库相关记录的对应关系。当新new的对象处于持久化状态时, 不允许程序随意修改它的ID。 
    save()方法介绍完了,那就应该实践一把,毕竟实践出真知。 
    我们最好新建一个普通java工程,如Hibernate_Test,然后在cn.itcast.h_session_method包下新建持久化类——User.java。

    public class User {
        private Integer id; // 0, null 如果是数字,建议使用包装类型。
        private String name;
        private byte[] data = new byte[1024 * 1024 * 10];
    
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
    • 1

    接着在cn.itcast.h_session_method包中创建User类对应的映射配置文件——User.hbm.xml。

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    
    <hibernate-mapping package="cn.itcast.h_session_method">
        <class name="User" table="user">
            <id name="id">
                <generator class="native"></generator> 
            </id>
            <property name="name" /> 
        </class>
    </hibernate-mapping>
    • 1
    • 2

    我们应该使案例更加简洁易懂点。接着我们就要编写单元测试类来试验save()方法了。

    public class App {
    
        private static SessionFactory sessionFactory = new Configuration() //
                .configure() //
                .addClass(User.class) // 添加Hibernate实体类(加载对应的映射文件)
                .buildSessionFactory();
    
        @Test
        public void testSave() {
            Session session = sessionFactory.openSession();
            session.beginTransaction();
            // -------------------------------------------
    
            User user = new User(); // 临时状态
            user.setName("test");
            session.save(user); // 变为了持久化状态
    
            user.setName("李四");
    
            // -------------------------------------------
            session.getTransaction().commit();
            session.close();
    
            System.out.println(user.getName()); // 游离状态
        }
    }
    • 1

    测试testSave()方法,数据库表记录为: 
     
    Eclipse控制台打印如下:

    Hibernate: insert into user (name) values (?) 
    Hibernate: update user set name=? where id=? 
    李四

    但是,若testSave()方法的代码改为:

    @Test
    public void testSave() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = new User(); // 临时状态
        user.setName("test");
        session.save(user); // 变为了持久化状态
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    
        user.setName("李四");
        System.out.println(user.getName()); // 游离状态
    }

    测试testSave()方法,数据库表记录为: 
    这里写图片描述 
    Eclipse控制台打印如下:

    Hibernate: insert into user (name) values (?) 
    李四

    save()方法是把临时状态转变为持久化状态,即把临时状态的对象放在Session缓存里面,交给Session管理,所以如下代码:

    @Test
    public void testSave() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = new User(); // 临时状态
        user.setName("test");
        session.save(user); // 变为了持久化状态
        session.save(user); // 变为了持久化状态
        session.save(user); // 变为了持久化状态
        session.save(user); // 变为了持久化状态
        session.save(user); // 变为了持久化状态
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    
        user.setName("李四");
        System.out.println(user.getName()); // 游离状态
    }

    Eclipse控制台只会打印一条insert into语句:

    Hibernate: insert into user (name) values (?) 
    李四

    注意:使用save()保存临时状态对象时,会立刻执行sql语句,而不是在事务提交的时候执行。例如,将testSave()方法的代码改为:

    @Test
    public void testSave() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = new User(); // 临时状态
        user.setName("test");
        session.save(user); // 变为了持久化状态
        System.out.println("xxxxxxxxxxx");
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    
        user.setName("李四");
        System.out.println(user.getName()); // 游离状态
    }

    Eclipse控制台将打印:

    Hibernate: insert into user (name) values (?) 
    xxxxxxxxxxx 
    李四

    update()

    Session的update()方法使一个游离对象转变为持久化对象,并且计划执行一条update语句。现在我们来试验update()方法,编写如下的testUpdate()方法:

    @Test
    public void testUpdate() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.get(User.class, 1);
        System.out.println(user.getName()); // 持久化状态
    
        session.clear(); // 清空Session中所有的对象,变为游离状态
        user.setName("newname"); // 更新语句只有在事务提交的时候执行
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    此时Eclipse控制台打印:

    Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=? 
    李四

    没有打印update语句。 
    但若我们将testUpdate()方法的代码修改为:

    @Test
    public void testUpdate() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.get(User.class, 1);
        System.out.println(user.getName()); // 持久化状态
    
        user.setName("newname"); // 更新语句只有在事务提交的时候执行
        session.clear(); // 清空Session中所有的对象,变为游离状态
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    发现Eclipse控制台仍然没有打印update语句。

    Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=? 
    李四

    原因是更新语句只有在事务提交的时候才执行。 
    但若强制更新语句马上执行,即testUpdate()方法的代码为:

    @Test
    public void testUpdate() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.get(User.class, 1);
        System.out.println(user.getName()); // 持久化状态
    
        user.setName("newname"); // 更新语句只有在事务提交的时候执行
        session.flush(); // 强制更新语句马上执行,刷出到数据库
    
        session.clear(); // 清空Session中所有的对象,变为游离状态
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    此时Eclipse控制台会打印update语句。

    Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=? 
    李四 
    Hibernate: update user set name=? where id=?

    但如果testUpdate()方法的代码为:

    @Test
    public void testUpdate() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.get(User.class, 1);
        System.out.println(user.getName()); // 持久化状态
    
        session.clear(); // 清空Session中所有的对象,变为游离状态
    
        user.setName("newname2"); // 更新语句只有在事务提交的时候执行
        session.flush(); // 强制更新语句马上执行,刷出到数据库
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    由于Session中的User对象变为游离状态,所以修改此状态对象时数据库不会有变化。Eclipse控制台会打印:

    Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=? 
    newname

    除了使用Session的clear()方法使Session中的User对象变为游离状态外,Session的evict(Object)方法同样也可以达到相同的目的。

    @Test
    public void testUpdate() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.get(User.class, 1);
        System.out.println(user.getName()); // 持久化状态
    
        session.evict(user); // 清除Session中一个指定的对象
    
        user.setName("newname2"); // 更新语句只有在事务提交的时候执行
        session.flush(); // 强制更新语句马上执行,刷出到数据库
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    Eclipse控制台打印结果同上:

    Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=? 
    newname

    如果testUpdate()方法的代码改为:

    @Test
    public void testUpdate() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.get(User.class, 1);
        System.out.println(user.getName()); // 持久化状态
    
        session.evict(user); // 清除Session中一个指定的对象
    
        user.setName("newname2"); // 更新语句只有在事务提交的时候执行
        session.update(user); // 变为了持久化状态
        session.flush(); // 强制更新语句马上执行,刷出到数据库
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    此时,Eclipse控制台打印:

    Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=? 
    newname 
    Hibernate: update user set name=? where id=?

    到这里,我们做一个小结——将Session中的方法归类:

    • 操作实体对象的 
      • save()
      • update()
      • saveOrUpdate()
      • delete()
    • 操作Session缓存的 
      • clear()
      • evict()
      • flush()
    • 查询实体对象的 
      • get()
      • load()
      • createQuery()
      • createCriteria()

    我们把flush()拧出来说一下,flush()的作用是马上执行sql语句,但sql语句默认是在事务提交的时候执行的,事务提交的时候会先默认调用flush(),在做删除、更新时都是这样,唯独有个另外,那就是在做保存的时候。例如,将testUpdate()方法的代码改为:

    @Test
    public void testUpdate() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.get(User.class, 1);
        System.out.println(user.getName()); // 持久化状态
    
        session.evict(user); // 清除Session中一个指定的对象
    
        user.setName("newname2"); // 更新语句只有在事务提交的时候执行
        session.update(user); // 变为了持久化状态
        System.out.println("+++++++++++");
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    此时,Eclipse控制台打印:

    Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=? 
    newname2 
    +++++++++++ 
    Hibernate: update user set name=? where id=?

    saveOrUpdate()

    该方法同时包含save和update方法,如果参数是临时对象就用save方法,如果是游离对象就用update方法,如果是持久化对象就直接返回。也即把临时或游离状态转为持久化状态。现在我们来试验saveOrUpdate()方法,编写如下的testsaveOrUpdate()方法:

    @Test
    public void testSaveOrUpdate() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = new User();
        user.setName("test_h");
    
        session.saveOrUpdate(user);
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    此时,Eclipse控制台打印如下结果:

    Hibernate: insert into user (name) values (?)

    如果再次测试testSaveOrUpdate(),Eclipse控制台仍然会打印一条insert into语句,向数据库表中插入一条记录。
    我们自己也可以模拟生成一个游离状态的对象,然后更新对象到数据库中。如下:

    @Test
    public void testSaveOrUpdate() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = new User();
        user.setId(3); // 自己模拟生成了一个游离状态的对象,数据库中要有一个id为3的记录
        user.setName("newName");
    
        session.saveOrUpdate(user);
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    发现Eclipse控制台打印如下结果:

    Hibernate: update user set name=? where id=?

    理所应当地,数据库表中id为3的记录name列将变为newName。注意:数据库表中要有一个id为3的记录。如果在更新的时候,对象不存在(即数据库表中没有相应的记录)就报错,如以下代码:

    @Test
    public void testSaveOrUpdate() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = new User();
        user.setId(300); // 自己模拟生成了一个游离状态的对象,数据库中要有一个id为3的记录
        user.setName("newName");
    
        session.saveOrUpdate(user);
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    将会报异常:org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1。 
    注意:本方法是根据id判断对象是什么状态的:如果id为原始值(对象是null,原始类型数字是0)就是临时状态,如果不是原始值就是游离状态

    delete()

    Session的delete()方法既可以删除一个游离对象,也可以删除一个持久化对象。即把持久化或游离状态转为删除状态。

    • 如果参数是持久化对象,就执行一个delete语句,若为游离对象,先使游离对象被session关联,使它变为持久化对象。
    • 计划执行一条delete语句。
    • 把对象从Session缓存中删除,该对象进入删除状态。

    现在我们来试验delete()方法,编写如下的testDelete()方法:

    @Test
    public void testDelete() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.get(User.class, 1); // 持久化状态
    
        session.delete(user);
        System.out.println("+++++++++");
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    测试testDelete()方法,会发现Eclipse控制台打印:

    Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=? 
    +++++++++ 
    Hibernate: delete from user where id=?

    若将testDelete()方法的代码改为:

    @Test
    public void testDelete() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.get(User.class, 2); // 持久化状态
    
        session.delete(user);
        session.flush();
        System.out.println("+++++++++");
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    再次测试testDelete()方法,会发现Eclipse控制台打印:

    Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=? 
    Hibernate: delete from user where id=? 
    +++++++++

    上面例子是使用delete()方法删除一个持久化对象,现在我们来使用delete()方法删除一个游离对象,须修改testDelete()方法的代码为:

    @Test
    public void testDelete() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        // 模拟一个游离状态的对象并删除
        User user = new User();
        user.setId(3);
    
        session.delete(user);
        session.flush();
        System.out.println("+++++++++");
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    再次测试testDelete()方法,会发现Eclipse控制台打印:

    Hibernate: delete from user where id=? 
    +++++++++

    注意:我们在删除的时候,如果删除的对象不存在就会抛异常。如将testDelete()方法的代码改为:

    @Test
    public void testDelete() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        // 模拟一个游离状态的对象并删除
        User user = new User();
        user.setId(300);
    
        session.delete(user);
        session.flush();
        System.out.println("+++++++++");
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    再次测试testDelete()方法,会发现抛异常:org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

    get()

    根据给定的OID从数据库中加载一个持久化对象。 
    现在我们来试验get()方法,编写如下的testGet()方法:

    @Test
    public void testGet() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.get(User.class, 5); // 持久化状态
        System.out.println(user.getName());
        System.out.println("+++++++++++");  
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    测试以上方法,Eclipse控制台打印:

    Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=? 
    test_h 
    +++++++++++

    这说明一执行查询,会马上执行sql语句,因为有可能马上就要用到,其他的操作(删除或者更新)都在flush()时执行sql语句。并且如果对象数据不存在,就返回null。

    load()

    load()和get()非常相似,也能根据给定的OID从数据库中加载一个持久化对象,但是区别真是天差地别啊! 
    现在我们来试验load()方法,编写如下的testLoad()方法:

    @Test
    public void testLoad() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.load(User.class, 5); // 持久化状态
        System.out.println("+++++++++++++");
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    测试以上方法,发现Eclipse控制台没有打印任何sql语句。 
    但若将testLoad()方法的代码改为:

    @Test
    public void testLoad() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.load(User.class, 5); // 持久化状态
        System.out.println("+++++++++++++");
        System.out.println(user.getName());
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    再次测试以上方法,发现Eclipse控制台会打印sql查询语句:

    +++++++++++++ 
    Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=? 
    test_h

    如果我要是将testLoad()方法的代码改为:

    @Test
    public void testLoad() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.load(User.class, 5); // 持久化状态
        System.out.println("+++++++++++++");
        System.out.println(user.getName());
        System.out.println(user.getName());
        System.out.println(user.getName());
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    测试以上方法,Eclipse控制台打印:

    +++++++++++++ 
    Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=? 
    test_h 
    test_h 
    test_h

    当getName()很多次时,并不会每次都去数据库里查找,而是查找一次就可以了,将查找到的对象放在Session中。 
    若将testLoad()方法的代码改为:

    @Test
    public void testLoad() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.load(User.class, 5); // 持久化状态
        System.out.println("+++++++++++++");
        System.out.println(user.getId());
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    测试以上方法,Eclipse控制台打印:

    +++++++++++++ 
    5

    这说明一执行查询,不会马上执行sql语句,而是在第一次使用非id或class属性时执行sql语句。 
    如若将testLoad()方法的代码改为:

    @Test
    public void testLoad() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.load(User.class, 500); // 持久化状态
        System.out.println("+++++++++++++");
        System.out.println(user.getId());
        System.out.println(user.getName());
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    此时,测试以上方法,会报异常:org.hibernate.ObjectNotFoundException。 
    这说明如果对象数据不存在,就要抛异常——org.hibernate.ObjectNotFoundException。因为获取的时候并没有真正的查询,但还是返回一个对象,返回的是一个代理对象,你这个时候拿这个对象就用了,用了半天,结果发现一执行不存在这个对象,所以就要抛异常。如果你要有这个对象,就得有一个前提,假定它一定是存在的,如果你不能断定,就要调用get()方法先判断该对象是否为空,再做操作。 
    若将testLoad()方法的代码改为:

    @Test
    public void testLoad() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------
    
        User user = (User) session.load(User.class, 5); // 持久化状态
        System.out.println(user.getClass()); // load()返回的是一个代理对象,通过子类的方式进行增强
        System.out.println("+++++++++++++");
        System.out.println(user.getId());
        System.out.println(user.getName());
    
        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }

    此时,测试以上方法,Eclipse控制台打印:

    class cn.itcast.h_session_method.User_$$_jvst57_0 
    +++++++++++++ 

    Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=? 
    test_h

    这说明load()后返回的是一个代理对象,要求类不能是final的,否则不能生成子类代理,就不能使用懒加载功能了。让懒加载失效的两种方式:

    1. 把实体类写成final的。
    2. 在hbm.xml中写<class ... lazy="false">,lazy属性默认为true,默认可以懒加载。

    get()和load()的区别

    方法加载方式返回值如果数据不存在
    get() 立即加载 真实对象或null 返回null
    load() 延迟加载 代理对象 抛异常
  • 相关阅读:
    Kali,CentOS 配置静态网络与开启SSH服务【附VMware中配置】
    httpHelper请求辅助类
    请求后的数据处理
    Viewcontroller基类
    上拉下拉基类
    获取cell中的button在整个屏幕上的位置
    Object-C反射读取实体属性和值
    xcode在代码中查找中文
    编写xcode5插件需要增加DVTPlugInCompatibilityUUIDs
    c# 扩展方法
  • 原文地址:https://www.cnblogs.com/telwanggs/p/6963494.html
Copyright © 2011-2022 走看看