hibernate学习(持久化对象生命周期)

hibernate是帮助程序猿用面向对象的思想来操作数据库的,需要涉及到这些对象的各种状态,下面是我总结的一张图:

官方提供的三种状态的转换图:
hibernate学习(持久化对象生命周期)_第1张图片

save和saveOrUpdate方法的区别

可以看到save和saveOrUpdate都可以讲瞬时对象转换成持久化对象,可是他们之间的区别是什么呢??
1.save方法,每次会将瞬时对象都存入数据库当中。
2.saveOrUpdate方法,会首先判断数据库当中是否存在主键相同的对象,如果存在,则进行更新操作,否则执行save操作。

update和merge的区别

先来看看下面的代码:

transaction = session.beginTransaction();
UserInfo info = new UserInfo();
info.setUserId(8);
info.setUserName("小四");
info.setUserPass("四四");
session.get(UserInfo.class,8);
session.update(info);
transaction.commit();

此时hibernate系统会抛出如下异常:
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
意思是在session缓存当中已经存在了一个id=8的持久化对象。并且事务回滚,更新失败

这次,我换成merge方法:

transaction = session.beginTransaction();
UserInfo info = new UserInfo();
info.setUserId(8);
info.setUserName("xiaowu");
info.setUserPass("666");
session.get(UserInfo.class,8);
session.merge(info);
transaction.commit();

可以看到这次成功的执行了更新操作。不同之处在于,merge方法是将瞬时的对象的主键,在session缓存当中查找,如果缓存当中存在一个持久化对象的主键是一样的,则将该瞬时对象的值赋值给该持久化对象。
总结:当session缓存当中存在两个id相同的对象,update操作会抛出异常。

evict,close,clear方法

这三个方法都是从缓存里边将实体对象删除,此时这些对象会从持久对象转换成离线对象。
close方法是关闭session
clear方法是不关闭session,只将缓存清除
evict是将一个特定的对象从持久对象转换成离线对象
举个例子:

Transaction transaction = null;
try {
    transaction = session.beginTransaction();
    Query query = session.createQuery("from UserInfo");
    List<UserInfo>lists = query.list();
    for (UserInfo userInfo : lists) {
        userInfo.setUserName("username");
    }
    transaction.commit();
} catch (HibernateException e) {
    if (transaction != null) {
        transaction.rollback();
    }
    e.printStackTrace();
} finally {
    if (session != null) {
        session.close();
    }
}

此时所有的数据的name都已经改变为”username”,是所以这些对象的更新都会同步到数据库,是因为他们都是存在session缓存当中的,都是持久对象。如果此时我在for循环之前将session.clear(),这时这些对象的更新就不会同步到数据库,这是因为,此时session缓存已经清空,这些对象都已经改变为离线对象。如下:

transaction = session.beginTransaction();
Query query = session.createQuery("from UserInfo");
List<UserInfo>lists = query.list();
session.clear();
for (UserInfo userInfo : lists) {
    userInfo.setUserName("newname");
}
transaction.commit();

可是如果只是需要某一个对象,改为离线对象怎么办?这时可以使用evict方法,比如,我只是不想更改userpass=555的对象:

transaction = session.beginTransaction();
Query query = session.createQuery("from UserInfo");
List<UserInfo>lists = query.list();
for (UserInfo userInfo : lists) {
    if (userInfo.getUserPass().equals("555")) {
        session.evict(userInfo);
    }
    userInfo.setUserName("newname");
}

get和load的区别

get和load这两个方法都是用来查询对象的,有什么区别呢??

UserInfo user = null;
user = session.get(UserInfo.class,1);
user = session.load(UserInfo.class,1);

同样是查询id=1的数据,get方法会立即查询并返回,而load方法并不会立即查询,而是返回一个代理对象,这个代理对象是没有值的,他会在系统比较空闲的时候才发出一条sql语句,或者是我需要用到user对象的时候采取查询。这样可以提高系统的性能。

可以看到load方法默认是懒加载,那么如果不想懒加载,可以在实体类的映射文件中配置lazy=”false”即可。

<hibernate-mapping>
    <!-- name="全类名" table="表名" -->
    <class name="com.mydb.entity.UserInfo" table="userinfo" lazy="false">
        <!-- name="属性名" column="主键"-->
        <id name="userId" column="uid">
            <!-- 主键的生成策略:native(主键自增),assigned(指派) -->
            <generator class="assigned"></generator>
        </id>
        <!-- name="属性名" column="字段名" -->
        <property name="userName" column="uname"></property>
        <property name="userPass" column="upass"></property>
    </class>
</hibernate-mapping>    

如果上面的代码我们查询的是一个不存在的记录:

UserInfo user = null;
user = session.get(UserInfo.class,200);
user = session.load(UserInfo.class,200);

此时,get方法会返回null,而load方法会抛出一个异常:
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.mydb.entity.UserInfo#222]
这是因为,hibernate会试图为该user对象创建一个代理,然而此时user == null,所以会抛出异常。

Hibernate list和iterator的区别

其实list和iterator方法都会返回所有的数据,我们先看看query.list方法,其实list方法返回给用户一个list集合,代码如下:

//解析hibernate.cfg.xml文件
Configuration cfg = new Configuration().configure();
//创建SessionFactory(创建连接池)
SessionFactory factory = cfg.buildSessionFactory();
//创建session
Session session = factory.openSession();
Query query = session.createQuery("from UserInfo");
List<UserInfo>list = query.list();
for (UserInfo userInfo : list) {
    System.out.println(userInfo);
}

此时会查询出UserInfo实体类对应的数据库表中的所有数据。
此时控制台打印的sql语句和查询结果如下:
hibernate学习(持久化对象生命周期)_第2张图片
下面看看iterator方法:

//解析hibernate.cfg.xml文件
Configuration cfg = new Configuration().configure();
//创建SessionFactory(创建连接池)
SessionFactory factory = cfg.buildSessionFactory();
//创建session
Session session = factory.openSession();
Query query = session.createQuery("from UserInfo");
Iterator<UserInfo>iterator = query.iterate();
while (iterator.hasNext()) {
    UserInfo userInfo = (UserInfo) iterator.next();
    System.out.println(userInfo);
}

此时控制台打印的sql语句和结果如下:
hibernate学习(持久化对象生命周期)_第3张图片
根据控制台打印的sql语句,可以发现虽然这两个方法都是查询出表里面的所有记录,但是list方法只发出了一条sql语句,然而iterator方法是首先查询所有记录的id,然后根据id在依次查询每一条记录。

那么既然iterator会发出多条查询语句,为什么不直接使用list来查询所有,注意:list方法虽然可以查询所有,并且只发出一条sql语句,但是list方法是不能利用缓存的数据,也就是说每一次查询都会发出查询所有的sql语句,比如我执行两次list方法,就会发出两条查询所有的语句,如下:

List<UserInfo>list = query.list();
List<UserInfo>list2 = query.list();

这里写图片描述
再看看iterator方法执行两次的情况:

Query query = session.createQuery("from UserInfo");
Iterator<UserInfo>iterator = query.iterate();
while (iterator.hasNext()) {
    UserInfo userInfo = (UserInfo) iterator.next();
    System.out.println(userInfo);
}
Iterator<UserInfo>iterator2 = query.iterate();
while (iterator2.hasNext()) {
    UserInfo userInfo = (UserInfo) iterator2.next();
    System.out.println(userInfo);
}

此时控制台打印如下:
hibernate学习(持久化对象生命周期)_第4张图片
根据控制台的打印结果,可以看出,当iterator方法,会将查询的每一条记录进行缓存,当多次执行iterator方法的时候,只会查询所有的id,然后根据id直接在缓存当中查找即可。

根据上面的分析,当我们查询所有数据的合理组合是首先利用list查询所有数据,此时数据都已经缓存到内存当中了,然后利用iterator方法来查询所有即可,这样会发出最少的查询语句,以此来提高性能。

你可能感兴趣的:(Hibernate,数据库,持久化对象)