Hibernate3 高级查询

来源:互联网 发布:怎么让淘宝客户加微信 编辑:程序博客网 时间:2024/05/05 04:56
Top

JAVA Hibernate DAY03

  1. 使用多对一关联映射
  2. 关联查询的一些特性
  3. 级联添加/修改
  4. 级联删除
  5. HQL查询,按条件查询
  6. HQL查询,查询一部分字段
  7. HQL查询,分页查询
  8. HQL查询,多表联合查询
  9. Hibernate中的SQL查询
  10. 使用二级缓存
  11. 使用查询缓存

1 使用多对一关联映射

1.1 问题

使用多对一关联映射,在查询业务账号时,自动查询出它对应的账务账号。

1.2 方案

多对一关联映射开发步骤:

  1. 业务账号与账务账号具有多对一关系,他们的关系字段是service.account_id。
  2. 在业务账号中追加Account类型的属性,用于封装它对应的唯一账务账号。
  3. 在业务账号映射关系文件中配置此属性。

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:在业务账号实体类中追加属性

在业务账号实体类Service中,追加Account类型的属性,用于封装它对应的唯一账务账号。由于这个属性包含了账务账号的ID,因此accountId属性可以去掉了,实际上这个属性必须去掉,否则会报错。代码如下:

  1. package com.tarena.entity;
  2. import java.sql.Date;
  3. public class Service {
  4.     private Integer id;
  5. //    private Integer accountId;
  6.     private String unixHost;
  7.     private String osUserName;
  8.     private String loginPassword;
  9.     private String status;
  10.     private Date createDate;
  11.     private Date pauseDate;
  12.     private Date closeDate;
  13.     private Integer costId;
  14.     // 追加属性,用于封装对应的Account记录
  15.     private Account account;
  16.     public Integer getId() {
  17.         return id;
  18.     }
  19.     public void setId(Integer id) {
  20.         this.id = id;
  21.     }
  22. //    public Integer getAccountId() {
  23. //        return accountId;
  24. //    }
  25. //
  26. //    public void setAccountId(Integer accountId) {
  27. //        this.accountId = accountId;
  28. //    }
  29.     public Account getAccount() {
  30.         return account;
  31.     }
  32.     public void setAccount(Account account) {
  33.         this.account = account;
  34.     }
  35.     public String getUnixHost() {
  36.         return unixHost;
  37.     }
  38.     public void setUnixHost(String unixHost) {
  39.         this.unixHost = unixHost;
  40.     }
  41.     public String getOsUserName() {
  42.         return osUserName;
  43.     }
  44.     public void setOsUserName(String osUserName) {
  45.         this.osUserName = osUserName;
  46.     }
  47.     public String getLoginPassword() {
  48.         return loginPassword;
  49.     }
  50.     public void setLoginPassword(String loginPassword) {
  51.         this.loginPassword = loginPassword;
  52.     }
  53.     public String getStatus() {
  54.         return status;
  55.     }
  56.     public void setStatus(String status) {
  57.         this.status = status;
  58.     }
  59.     public Date getCreateDate() {
  60.         return createDate;
  61.     }
  62.     public void setCreateDate(Date createDate) {
  63.         this.createDate = createDate;
  64.     }
  65.     public Date getPauseDate() {
  66.         return pauseDate;
  67.     }
  68.     public void setPauseDate(Date pauseDate) {
  69.         this.pauseDate = pauseDate;
  70.     }
  71.     public Date getCloseDate() {
  72.         return closeDate;
  73.     }
  74.     public void setCloseDate(Date closeDate) {
  75.         this.closeDate = closeDate;
  76.     }
  77.     public Integer getCostId() {
  78.         return costId;
  79.     }
  80.     public void setCostId(Integer costId) {
  81.         this.costId = costId;
  82.     }
  83. }

步骤二:在业务账号映射关系文件中配置这个属性

在业务账号映射关系文件service.hbm.xml中,配置这个关联属性,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Service" table="SERVICE">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">SERVICE_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <!--
  12.             由于account属性已经体现了业务账号与账务账号的关系,
  13.             并且account属性可以包含账务账号ID,因此accountId可以去掉,
  14.             实际上这里必须去掉这个属性的配置,否则会报错。
  15.         -->
  16.         <!-- <property name="accountId"
  17.             type="integer" column="ACCOUNT_ID"/> -->
  18.         <property name="unixHost"
  19.             type="string" column="UNIX_HOST"/>
  20.         <property name="osUserName"
  21.             type="string" column="OS_USERNAME"/>
  22.         <property name="loginPassword"
  23.             type="string" column="LOGIN_PASSWD"/>
  24.         <property name="status"
  25.             type="string" column="STATUS"/>
  26.         <property name="createDate"
  27.             type="date" column="CREATE_DATE"/>
  28.         <property name="pauseDate"
  29.             type="date" column="PAUSE_DATE"/>
  30.         <property name="closeDate"
  31.             type="date" column="CLOSE_DATE"/>
  32.         <property name="costId"
  33.             type="integer" column="COST_ID"/>
  34.             
  35.         <!-- 配置account属性,采用多对一关系加载相关的account内容 -->
  36.         <many-to-one name="account" column="ACCOUNT_ID"
  37.             class="com.tarena.entity.Account"/>            
  38.     </class>
  39. </hibernate-mapping>

步骤三:创建测试类

在com.tarena.test包下,创建一个测试类TestManyToOne,并且增加一个测试方法。在方法中查询出一条业务账号数据,然后输出这个业务账号的一些属性,同时输出account属性的值。代码如下:

  1. package com.tarena.test;
  2. import org.hibernate.Session;
  3. import org.junit.Test;
  4. import com.tarena.entity.Service;
  5. import com.tarena.util.HibernateUtil;
  6. public class TestManyToOne {
  7.     
  8.     @Test
  9.     public void test1() {
  10.         Session session = HibernateUtil.getSession();
  11.         Service service =
  12.             (Service) session.get(Service.class, 2001);
  13.         System.out.println(service.getOsUserName());
  14.         System.out.println("------------------");
  15.         System.out.println(
  16.                 service.getAccount().getId() + " " +
  17.                 service.getAccount().getIdcardNo());
  18.     }
  19.     
  20. }

步骤四:测试

执行这个测试方法,控制台输出结果如下图,可以看出在查询SERVICE的同时,Hibernate自动查询出了它对应的ACCOUNT数据,并且这个关联查询是采用延迟加载机制实现的。

图-1

1.4 完整代码

以下为本案例的完整代码。

其中业务账号实体类完整代码如下:

代码

业务账号映射关系文件代码如下:

代码

测试类代码如下:

代码

2 关联查询的一些特性

2.1 问题

请按照如下的方式使用关联映射:

  1. 不采用延迟加载的方式查询关联属性。
  2. 采用关联查询一次性查出2张表的数据。

2.2 方案

可以按照如下的方式实现上述问题的要求:

  1. 通过lazy=”false”取消延迟加载。
  2. 通过fetch=”join”设置关联查询。

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建项目

复制项目HibernateDay02,粘贴并修改项目名为HibernateDay03。

步骤二:将一对多关联映射取消延迟加载

修改HibernateDay03项目中的Account.hbm.xml文件,将services属性的配置追加lazy=”false”,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Account" table="ACCOUNT">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">ACCOUNT_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <property name="recommenderId"
  12.             type="integer" column="RECOMMENDER_ID"/>
  13.         <property name="loginName"
  14.             type="string" column="LOGIN_NAME"/>
  15.         <property name="loginPassword"
  16.             type="string" column="LOGIN_PASSWD"/>
  17.         <property name="status"
  18.             type="string" column="STATUS"/>
  19.         <property name="createDate"
  20.             type="date" column="CREATE_DATE"/>
  21.         <property name="pauseDate"
  22.             type="date" column="PAUSE_DATE"/>
  23.         <property name="closeDate"
  24.             type="date" column="CLOSE_DATE"/>
  25.         <property name="realName"
  26.             type="string" column="REAL_NAME"/>
  27.         <property name="idcardNo"
  28.             type="string" column="IDCARD_NO"/>
  29.         <property name="birthdate"
  30.             type="date" column="BIRTHDATE"/>
  31.         <property name="gender"
  32.             type="string" column="GENDER"/>
  33.         <property name="occupation"
  34.             type="string" column="OCCUPATION"/>
  35.         <property name="telephone"
  36.             type="string" column="TELEPHONE"/>
  37.         <property name="email"
  38.             type="string" column="EMAIL"/>
  39.         <property name="mailaddress"
  40.             type="string" column="MAILADDRESS"/>
  41.         <property name="zipcode"
  42.             type="string" column="ZIPCODE"/>
  43.         <property name="qq"
  44.             type="string" column="QQ"/>
  45.         <property name="lastLoginTime"
  46.             type="date" column="LAST_LOGIN_TIME"/>
  47.         <property name="lastLoginIp"
  48.             type="string" column="LAST_LOGIN_IP"/>
  49.             
  50.         <!-- 配置services属性,采用一对多的关系 -->
  51.         <set name="services" lazy="false">
  52.             <!-- 用于指定关联条件,写关联条件的外键字段 -->
  53.             <key column="ACCOUNT_ID"/>
  54.             <!-- 用于指定采用哪种关系,加载哪方数据 -->
  55.             <one-to-many class="com.tarena.entity.Service"/>
  56.         </set>
  57.     </class>
  58. </hibernate-mapping>

执行TestOneToMany中的测试方法,控制台输出结果如下图,可以看出在查询账务账号之后Hibernate立刻查询了它对应的业务账号,已经取消了延迟加载。

图-2

步骤三:将多对一关联映射取消延迟加载

修改Service.hbm.xml,将account属性的配置追加lazy=”false”,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Service" table="SERVICE">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">SERVICE_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <!--
  12.             由于account属性已经体现了业务账号与账务账号的关系,
  13.             并且account属性可以包含账务账号ID,因此accountId可以去掉,
  14.             实际上这里必须去掉这个属性的配置,否则会报错。
  15.         -->
  16.         <!-- <property name="accountId"
  17.             type="integer" column="ACCOUNT_ID"/> -->
  18.         <property name="unixHost"
  19.             type="string" column="UNIX_HOST"/>
  20.         <property name="osUserName"
  21.             type="string" column="OS_USERNAME"/>
  22.         <property name="loginPassword"
  23.             type="string" column="LOGIN_PASSWD"/>
  24.         <property name="status"
  25.             type="string" column="STATUS"/>
  26.         <property name="createDate"
  27.             type="date" column="CREATE_DATE"/>
  28.         <property name="pauseDate"
  29.             type="date" column="PAUSE_DATE"/>
  30.         <property name="closeDate"
  31.             type="date" column="CLOSE_DATE"/>
  32.         <property name="costId"
  33.             type="integer" column="COST_ID"/>
  34.             
  35.         <!-- 配置account属性,采用多对一关系加载相关的account内容 -->
  36.         <many-to-one name="account" column="ACCOUNT_ID"
  37.             class="com.tarena.entity.Account"
  38.             lazy="false"/>            
  39.     </class>
  40. </hibernate-mapping>

执行TestManyToOne中的测试方法,控制台输出效果如下图,可以看出在查询业务账号之后Hibernate立刻查询了对应的账务账号,取消了延迟加载。

图-3

步骤四:将一对多关联映射设置为join方式抓取数据

修改Account.hbm.xml,将services属性的配置追加fetch=“join”,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Account" table="ACCOUNT">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">ACCOUNT_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <property name="recommenderId"
  12.             type="integer" column="RECOMMENDER_ID"/>
  13.         <property name="loginName"
  14.             type="string" column="LOGIN_NAME"/>
  15.         <property name="loginPassword"
  16.             type="string" column="LOGIN_PASSWD"/>
  17.         <property name="status"
  18.             type="string" column="STATUS"/>
  19.         <property name="createDate"
  20.             type="date" column="CREATE_DATE"/>
  21.         <property name="pauseDate"
  22.             type="date" column="PAUSE_DATE"/>
  23.         <property name="closeDate"
  24.             type="date" column="CLOSE_DATE"/>
  25.         <property name="realName"
  26.             type="string" column="REAL_NAME"/>
  27.         <property name="idcardNo"
  28.             type="string" column="IDCARD_NO"/>
  29.         <property name="birthdate"
  30.             type="date" column="BIRTHDATE"/>
  31.         <property name="gender"
  32.             type="string" column="GENDER"/>
  33.         <property name="occupation"
  34.             type="string" column="OCCUPATION"/>
  35.         <property name="telephone"
  36.             type="string" column="TELEPHONE"/>
  37.         <property name="email"
  38.             type="string" column="EMAIL"/>
  39.         <property name="mailaddress"
  40.             type="string" column="MAILADDRESS"/>
  41.         <property name="zipcode"
  42.             type="string" column="ZIPCODE"/>
  43.         <property name="qq"
  44.             type="string" column="QQ"/>
  45.         <property name="lastLoginTime"
  46.             type="date" column="LAST_LOGIN_TIME"/>
  47.         <property name="lastLoginIp"
  48.             type="string" column="LAST_LOGIN_IP"/>
  49.             
  50.         <!-- 配置services属性,采用一对多的关系 -->
  51.         <set name="services" lazy="false" fetch="join">
  52.             <!-- 用于指定关联条件,写关联条件的外键字段 -->
  53.             <key column="ACCOUNT_ID"/>
  54.             <!-- 用于指定采用哪种关系,加载哪方数据 -->
  55.             <one-to-many class="com.tarena.entity.Service"/>
  56.         </set>
  57.     </class>
  58. </hibernate-mapping>

执行TestOneToMany中的测试方法,控制台输出结果如下图,可以看出查询时采用了join方式查询。

图-4

步骤五:将多对一关联映射设置为join方式抓取数据

修改Service.hbm.xml,将account属性追加fetch=“join”,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Service" table="SERVICE">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">SERVICE_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <!--
  12.             由于account属性已经体现了业务账号与账务账号的关系,
  13.             并且account属性可以包含账务账号ID,因此accountId可以去掉,
  14.             实际上这里必须去掉这个属性的配置,否则会报错。
  15.         -->
  16.         <!-- <property name="accountId"
  17.             type="integer" column="ACCOUNT_ID"/> -->
  18.         <property name="unixHost"
  19.             type="string" column="UNIX_HOST"/>
  20.         <property name="osUserName"
  21.             type="string" column="OS_USERNAME"/>
  22.         <property name="loginPassword"
  23.             type="string" column="LOGIN_PASSWD"/>
  24.         <property name="status"
  25.             type="string" column="STATUS"/>
  26.         <property name="createDate"
  27.             type="date" column="CREATE_DATE"/>
  28.         <property name="pauseDate"
  29.             type="date" column="PAUSE_DATE"/>
  30.         <property name="closeDate"
  31.             type="date" column="CLOSE_DATE"/>
  32.         <property name="costId"
  33.             type="integer" column="COST_ID"/>
  34.             
  35.         <!-- 配置account属性,采用多对一关系加载相关的account内容 -->
  36.         <many-to-one name="account" column="ACCOUNT_ID"
  37.             class="com.tarena.entity.Account"
  38.             lazy="false" fetch="join"/>            
  39.     </class>
  40. </hibernate-mapping>

执行TestManyToOne中的测试方法,控制台输出结果如下图,可以看出查询时采用了join方式查询。

图-5

2.4 完整代码

以下为本案例的完整代码。

其中账务账号映射关系文件完整代码如下:

代码

业务账号映射关系文件完整代码如下:

代码

3 级联添加/修改

3.1 问题

使用级联添加/修改,在添加/修改账务账号时,自动添加/修改其对应的业务账号。

3.2 方案

通过在映射关系文件中设置cascade=”save-update”,可以支持级联添加/修改。

3.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:在账务账号映射关系文件中设置级联添加/修改

在账务账号映射关系文件Account.hbm.xml中,通过cascade=“save-update”设置支持级联添加/修改,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Account" table="ACCOUNT">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">ACCOUNT_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <property name="recommenderId"
  12.             type="integer" column="RECOMMENDER_ID"/>
  13.         <property name="loginName"
  14.             type="string" column="LOGIN_NAME"/>
  15.         <property name="loginPassword"
  16.             type="string" column="LOGIN_PASSWD"/>
  17.         <property name="status"
  18.             type="string" column="STATUS"/>
  19.         <property name="createDate"
  20.             type="date" column="CREATE_DATE"/>
  21.         <property name="pauseDate"
  22.             type="date" column="PAUSE_DATE"/>
  23.         <property name="closeDate"
  24.             type="date" column="CLOSE_DATE"/>
  25.         <property name="realName"
  26.             type="string" column="REAL_NAME"/>
  27.         <property name="idcardNo"
  28.             type="string" column="IDCARD_NO"/>
  29.         <property name="birthdate"
  30.             type="date" column="BIRTHDATE"/>
  31.         <property name="gender"
  32.             type="string" column="GENDER"/>
  33.         <property name="occupation"
  34.             type="string" column="OCCUPATION"/>
  35.         <property name="telephone"
  36.             type="string" column="TELEPHONE"/>
  37.         <property name="email"
  38.             type="string" column="EMAIL"/>
  39.         <property name="mailaddress"
  40.             type="string" column="MAILADDRESS"/>
  41.         <property name="zipcode"
  42.             type="string" column="ZIPCODE"/>
  43.         <property name="qq"
  44.             type="string" column="QQ"/>
  45.         <property name="lastLoginTime"
  46.             type="date" column="LAST_LOGIN_TIME"/>
  47.         <property name="lastLoginIp"
  48.             type="string" column="LAST_LOGIN_IP"/>
  49.             
  50.         <!-- 配置services属性,采用一对多的关系 -->
  51.         <set name="services"
  52.             lazy="false" fetch="join"
  53.             cascade="save-update">
  54.             <!-- 用于指定关联条件,写关联条件的外键字段 -->
  55.             <key column="ACCOUNT_ID"/>
  56.             <!-- 用于指定采用哪种关系,加载哪方数据 -->
  57.             <one-to-many
  58.                 class="com.tarena.entity.Service"/>
  59.         </set>
  60.     </class>
  61. </hibernate-mapping>

步骤二:测试级联添加

在com.tarena.test包下,创建测试类TestCascade,并在类中增加测试级联添加的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.HashSet;
  3. import java.util.Set;
  4. import org.hibernate.HibernateException;
  5. import org.hibernate.Session;
  6. import org.hibernate.Transaction;
  7. import org.junit.Test;
  8. import com.tarena.entity.Account;
  9. import com.tarena.entity.Service;
  10. import com.tarena.util.HibernateUtil;
  11. public class TestCascade {
  12.     /**
  13.      * 级联添加
  14.      */
  15.     @Test
  16.     public void test1() {
  17.         // 模拟要添加的账务账号
  18.         Account a = new Account();
  19.         a.setLoginName("gg");
  20.         a.setLoginPassword("123");
  21.         a.setRealName("gg");
  22.         a.setIdcardNo("120392198410282549");
  23.         a.setStatus("0");
  24.         a.setTelephone("110");
  25.         // 模拟要添加的业务账号
  26.         Service s1 = new Service();
  27.         s1.setAccount(a);
  28.         s1.setOsUserName("gg1");
  29.         s1.setLoginPassword("123");
  30.         s1.setUnixHost("192.168.1.1");
  31.         s1.setCostId(5);
  32.         s1.setStatus("0");
  33.         Service s2 = new Service();
  34.         s2.setAccount(a);
  35.         s2.setOsUserName("gg2");
  36.         s2.setLoginPassword("123");
  37.         s2.setUnixHost("192.168.1.2");
  38.         s2.setCostId(5);
  39.         s2.setStatus("0");
  40.         // 将业务账号与账务账号关联
  41.         a.setServices(new HashSet<Service>());
  42.         a.getServices().add(s1);
  43.         a.getServices().add(s2);
  44.         Session session = HibernateUtil.getSession();
  45.         Transaction ts = session.beginTransaction();
  46.         try {
  47.             session.save(a);
  48.             ts.commit();
  49.         } catch (HibernateException e) {
  50.             e.printStackTrace();
  51.             ts.rollback();
  52.         } finally {
  53.             session.close();
  54.         }
  55.     }
  56.     
  57. }

执行test1()方法,控制台输出结果如下图,可以看出在新增账务账号之后,Hibernate自动新增了账务账号对应的业务账号数据,这就是级联添加所起到的作用。

图-6

此时,查询账务账号表,数据如下图,其中id=380的行就是刚刚添加的行。

图-7

再查询业务账号表,数据如下图,其中account_id=380的行就是级联添加的行。

图-8

步骤三:测试级联修改

在TestCascade测试类中,增加测试级联修改的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.HashSet;
  3. import java.util.Set;
  4. import org.hibernate.HibernateException;
  5. import org.hibernate.Session;
  6. import org.hibernate.Transaction;
  7. import org.junit.Test;
  8. import com.tarena.entity.Account;
  9. import com.tarena.entity.Service;
  10. import com.tarena.util.HibernateUtil;
  11. public class TestCascade {
  12.     /**
  13.      * 级联添加
  14.      */
  15.     @Test
  16.     public void test1() {
  17.         // 模拟要添加的账务账号
  18.         Account a = new Account();
  19.         a.setLoginName("gg");
  20.         a.setLoginPassword("123");
  21.         a.setRealName("gg");
  22.         a.setIdcardNo("120392198410282549");
  23.         a.setStatus("0");
  24.         a.setTelephone("110");
  25.         // 模拟要添加的业务账号
  26.         Service s1 = new Service();
  27.         s1.setAccount(a);
  28.         s1.setOsUserName("gg1");
  29.         s1.setLoginPassword("123");
  30.         s1.setUnixHost("192.168.1.1");
  31.         s1.setCostId(5);
  32.         s1.setStatus("0");
  33.         Service s2 = new Service();
  34.         s2.setAccount(a);
  35.         s2.setOsUserName("gg2");
  36.         s2.setLoginPassword("123");
  37.         s2.setUnixHost("192.168.1.2");
  38.         s2.setCostId(5);
  39.         s2.setStatus("0");
  40.         // 将业务账号与账务账号关联
  41.         a.setServices(new HashSet<Service>());
  42.         a.getServices().add(s1);
  43.         a.getServices().add(s2);
  44.         Session session = HibernateUtil.getSession();
  45.         Transaction ts = session.beginTransaction();
  46.         try {
  47.             session.save(a);
  48.             ts.commit();
  49.         } catch (HibernateException e) {
  50.             e.printStackTrace();
  51.             ts.rollback();
  52.         } finally {
  53.             session.close();
  54.         }
  55.     }
  56.     /**
  57.      * 级联修改
  58.      */
  59.     @Test
  60.     public void test2() {
  61.         Session session = HibernateUtil.getSession();
  62.         // 查询出要修改的账务账号
  63.         Account account =
  64. #cold_bold            (Account) session.get(Account.class, 380);
  65.         // 模拟对账务账号的修改
  66.         account.setLoginName("pp");
  67.         Set<Service> services = account.getServices();
  68.         for (Service service : services) {
  69.             // 模拟对业务账号的修改
  70.             service.setLoginPassword("pp");
  71.         }
  72.         Transaction ts = session.beginTransaction();
  73.         try {
  74.             session.update(account);
  75.             ts.commit();
  76.         } catch (HibernateException e) {
  77.             e.printStackTrace();
  78.             ts.rollback();
  79.         } finally {
  80.             session.close();
  81.         }
  82.     }
  83.     
  84. }

执行test2()方法后,控制台输出的结果如下图,可以看出在修改完账务账号之后,Hibernate自动修改了它对应的业务账号数据。

图-9

此时,查询账务账号表,id=380的行已经发生了改变。

图-10

再查询业务账号表,account_id=380的行也发生了改变。

图-11

3.4 完整代码

以下为本案例的完整代码。

其中Account.hbm.xml完整代码如下:

代码

TestCascade完整代码如下:

代码

4 级联删除

4.1 问题

使用级联删除,在删除账务账号时,自动删除其对应的业务账号。

4.2 方案

通过在映射关系文件中设置cascade=“delete”,可以支持级联删除,如果想全面支持级联添加、修改和删除,可以设置cascade=“all”。

4.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:在账务账号映射关系文件中设置级联删除

在账务账号映射关系文件Account.hbm.xml中,通过cascade=“all”设置支持级联删除,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Account" table="ACCOUNT">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">ACCOUNT_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <property name="recommenderId"
  12.             type="integer" column="RECOMMENDER_ID"/>
  13.         <property name="loginName"
  14.             type="string" column="LOGIN_NAME"/>
  15.         <property name="loginPassword"
  16.             type="string" column="LOGIN_PASSWD"/>
  17.         <property name="status"
  18.             type="string" column="STATUS"/>
  19.         <property name="createDate"
  20.             type="date" column="CREATE_DATE"/>
  21.         <property name="pauseDate"
  22.             type="date" column="PAUSE_DATE"/>
  23.         <property name="closeDate"
  24.             type="date" column="CLOSE_DATE"/>
  25.         <property name="realName"
  26.             type="string" column="REAL_NAME"/>
  27.         <property name="idcardNo"
  28.             type="string" column="IDCARD_NO"/>
  29.         <property name="birthdate"
  30.             type="date" column="BIRTHDATE"/>
  31.         <property name="gender"
  32.             type="string" column="GENDER"/>
  33.         <property name="occupation"
  34.             type="string" column="OCCUPATION"/>
  35.         <property name="telephone"
  36.             type="string" column="TELEPHONE"/>
  37.         <property name="email"
  38.             type="string" column="EMAIL"/>
  39.         <property name="mailaddress"
  40.             type="string" column="MAILADDRESS"/>
  41.         <property name="zipcode"
  42.             type="string" column="ZIPCODE"/>
  43.         <property name="qq"
  44.             type="string" column="QQ"/>
  45.         <property name="lastLoginTime"
  46.             type="date" column="LAST_LOGIN_TIME"/>
  47.         <property name="lastLoginIp"
  48.             type="string" column="LAST_LOGIN_IP"/>
  49.             
  50.         <!-- 配置services属性,采用一对多的关系 -->
  51.         <set name="services"
  52.             lazy="false" fetch="join"
  53.             cascade="all">
  54.             <!-- 用于指定关联条件,写关联条件的外键字段 -->
  55.             <key column="ACCOUNT_ID"/>
  56.             <!-- 用于指定采用哪种关系,加载哪方数据 -->
  57.             <one-to-many
  58.                 class="com.tarena.entity.Service"/>
  59.         </set>
  60.     </class>
  61. </hibernate-mapping>

步骤二:测试级联删除

在TestCascade测试类中,增加测试级联删除的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.HashSet;
  3. import java.util.Set;
  4. import org.hibernate.HibernateException;
  5. import org.hibernate.Session;
  6. import org.hibernate.Transaction;
  7. import org.junit.Test;
  8. import com.tarena.entity.Account;
  9. import com.tarena.entity.Service;
  10. import com.tarena.util.HibernateUtil;
  11. public class TestCascade {
  12.     /**
  13.      * 级联添加
  14.      */
  15.     @Test
  16.     public void test1() {
  17.         // 模拟要添加的账务账号
  18.         Account a = new Account();
  19.         a.setLoginName("gg");
  20.         a.setLoginPassword("123");
  21.         a.setRealName("gg");
  22.         a.setIdcardNo("120392198410282549");
  23.         a.setStatus("0");
  24.         a.setTelephone("110");
  25.         // 模拟要添加的业务账号
  26.         Service s1 = new Service();
  27.         s1.setAccount(a);
  28.         s1.setOsUserName("gg1");
  29.         s1.setLoginPassword("123");
  30.         s1.setUnixHost("192.168.1.1");
  31.         s1.setCostId(5);
  32.         s1.setStatus("0");
  33.         Service s2 = new Service();
  34.         s2.setAccount(a);
  35.         s2.setOsUserName("gg2");
  36.         s2.setLoginPassword("123");
  37.         s2.setUnixHost("192.168.1.2");
  38.         s2.setCostId(5);
  39.         s2.setStatus("0");
  40.         // 将业务账号与账务账号关联
  41.         a.setServices(new HashSet<Service>());
  42.         a.getServices().add(s1);
  43.         a.getServices().add(s2);
  44.         Session session = HibernateUtil.getSession();
  45.         Transaction ts = session.beginTransaction();
  46.         try {
  47.             session.save(a);
  48.             ts.commit();
  49.         } catch (HibernateException e) {
  50.             e.printStackTrace();
  51.             ts.rollback();
  52.         } finally {
  53.             session.close();
  54.         }
  55.     }
  56.     /**
  57.      * 级联修改
  58.      */
  59.     @Test
  60.     public void test2() {
  61.         Session session = HibernateUtil.getSession();
  62.         // 查询出要修改的账务账号
  63.         Account account =
  64.             (Account) session.get(Account.class, 380);
  65.         // 模拟对账务账号的修改
  66.         account.setLoginName("pp");
  67.         Set<Service> services = account.getServices();
  68.         for (Service service : services) {
  69.             // 模拟对业务账号的修改
  70.             service.setLoginPassword("pp");
  71.         }
  72.         Transaction ts = session.beginTransaction();
  73.         try {
  74.             session.update(account);
  75.             ts.commit();
  76.         } catch (HibernateException e) {
  77.             e.printStackTrace();
  78.             ts.rollback();
  79.         } finally {
  80.             session.close();
  81.         }
  82.     }
  83.     /**
  84.      * 级联删除
  85.      */
  86.     @Test
  87.     public void test3() {
  88.         Session session = HibernateUtil.getSession();
  89.         Account account = (Account) session.get(Account.class, 380);
  90.         Transaction ts = session.beginTransaction();
  91.         try {
  92.             session.delete(account);
  93.             ts.commit();
  94.         } catch (HibernateException e) {
  95.             e.printStackTrace();
  96.             ts.rollback();
  97.         } finally {
  98.             session.close();
  99.         }
  100.     }
  101.     
  102. }

步骤三:测试

执行test3()方法,控制台输出结果如下图,可以看出程序在运行时报错了:

图-12

步骤四:排错

上述错误是执行第二个SQL时产生的,这个SQL的目的是维护关系字段,将其置为null,而这个外键字段存在非空约束,因此报错。类似的事情在级联添加时也看到过,参考图-6,不同的是级联添加时要将关联字段设置为新生成的账务账号ID。然而,在级联新增或删除时业务账号时,业务账号本身已经维护好了关联字段,因此这个额外的操作是多余的,可以去掉,我们可以在关联属性上通过inverse=“true“去掉这个行为,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Account" table="ACCOUNT">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">ACCOUNT_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <property name="recommenderId"
  12.             type="integer" column="RECOMMENDER_ID"/>
  13.         <property name="loginName"
  14.             type="string" column="LOGIN_NAME"/>
  15.         <property name="loginPassword"
  16.             type="string" column="LOGIN_PASSWD"/>
  17.         <property name="status"
  18.             type="string" column="STATUS"/>
  19.         <property name="createDate"
  20.             type="date" column="CREATE_DATE"/>
  21.         <property name="pauseDate"
  22.             type="date" column="PAUSE_DATE"/>
  23.         <property name="closeDate"
  24.             type="date" column="CLOSE_DATE"/>
  25.         <property name="realName"
  26.             type="string" column="REAL_NAME"/>
  27.         <property name="idcardNo"
  28.             type="string" column="IDCARD_NO"/>
  29.         <property name="birthdate"
  30.             type="date" column="BIRTHDATE"/>
  31.         <property name="gender"
  32.             type="string" column="GENDER"/>
  33.         <property name="occupation"
  34.             type="string" column="OCCUPATION"/>
  35.         <property name="telephone"
  36.             type="string" column="TELEPHONE"/>
  37.         <property name="email"
  38.             type="string" column="EMAIL"/>
  39.         <property name="mailaddress"
  40.             type="string" column="MAILADDRESS"/>
  41.         <property name="zipcode"
  42.             type="string" column="ZIPCODE"/>
  43.         <property name="qq"
  44.             type="string" column="QQ"/>
  45.         <property name="lastLoginTime"
  46.             type="date" column="LAST_LOGIN_TIME"/>
  47.         <property name="lastLoginIp"
  48.             type="string" column="LAST_LOGIN_IP"/>
  49.             
  50.         <!-- 配置services属性,采用一对多的关系 -->
  51.         <set name="services"
  52.             lazy="false" fetch="join"
  53.             cascade="all" inverse="true">
  54.             <!-- 用于指定关联条件,写关联条件的外键字段 -->
  55.             <key column="ACCOUNT_ID"/>
  56.             <!-- 用于指定采用哪种关系,加载哪方数据 -->
  57.             <one-to-many
  58.                 class="com.tarena.entity.Service"/>
  59.         </set>
  60.     </class>
  61. </hibernate-mapping>

步骤五:测试

再次执行test3()方法,控制台输出结果如下图,可以看出本次删除成功了,不但删除了账务账号数据,在此之前还删除了它所对应的所有业务账号。

图-13

4.4 完整代码

以下为本案例的完整代码。

其中Account.hbm.xml完整代码如下:

代码

TestCascade完整代码如下:

代码

5 HQL查询,按条件查询

5.1 问题

使用带条件的HQL 查询业务账号数据。

5.2 方案

在HQL中拼入条件,如name=?,然后在查询之前使用query给参数赋值。

5.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写按条件查询方法

在com.tarena.test包下创建测试类TestHQL,并在类中增加按条件查询的测试方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10.     /**
  11.      * 按条件查询
  12.      */
  13.     @Test
  14.     public void test1() {
  15.         String hql = "from Service where unixHost=?";
  16.         Session session = HibernateUtil.getSession();
  17.         Query query = session.createQuery(hql);
  18.         query.setString(0, "192.168.0.20");
  19.         List<Service> services = query.list();
  20.         for(Service service : services){
  21.             System.out.println(service.getId()
  22.                     + " " + service.getUnixHost()
  23.                     + " " + service.getOsUserName());
  24.         }
  25.         session.close();
  26.     }
  27.     
  28. }

步骤二:测试

执行test1()方法,控制台输出效果如下图,可以看到按条件查询出的结果:

图-14

5.4 完整代码

以下为本案例的完整代码。

其中TestHQL完整代码如下:

代码

6 HQL查询,查询一部分字段

6.1 问题

使用HQL查询,要求只查询一部分字段。

6.2 方案

可以通过select子句明确指定要返回的字段,注意HQL中select子句中写的是属性,而不是表中的字段。

6.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写查询一部分字段方法

在TestHQL中增加查询一部分字段的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10. // 其他查询方法略
  11.     /**
  12.      * 查询一部分字段
  13.      */
  14.     @Test
  15.     public void test2() {
  16.         String hql = "select id,unixHost,osUserName " +
  17.                 "from Service where unixHost=?";
  18.         Session session = HibernateUtil.getSession();
  19.         Query query = session.createQuery(hql);
  20.         query.setString(0, "192.168.0.20");
  21.         List<Object[]> services = query.list();
  22.         for(Object[] service : services) {
  23.             System.out.println(service[0]
  24.                     + " " + service[1]
  25.                     + " " + service[2]);
  26.         }
  27.         session.close();
  28.     }
  29.     
  30. }

步骤二:测试

执行test2(),控制台输出结果如下图,可以看到查询出来的这些字段的值。

图-15

6.4 完整代码

以下为本案例的完整代码。

其中TestHQL完整代码如下:

代码

7 HQL查询,分页查询

7.1 问题

按照每页显示3条数据,查询第1页的条件,查询出业务账号表中满足条件的记录,并且查询出总页数。

7.2 方案

  1. 分页查询时,在执行查询之前可以通过query对象设置分页参数值,query.setFirstResult(起点),以及query.setMaxResults(页容量)。
  2. 查询总页数,只需要通过hql查询出总行数,再根据总行数计算总页数即可,而查询总行数的hql格式为select count(*) from 对象。

7.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写分页查询的方法

在TestHQL中,增加分页查询的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10.     // 其他查询方法略
  11.     
  12.     /**
  13.      * 分页查询
  14.      */
  15.     @Test
  16.     public void test3() {
  17.         int page = 1;
  18.         int pageSize = 3;
  19.         String hql = "from Service order by id";
  20.         Session session = HibernateUtil.getSession();
  21.         Query query = session.createQuery(hql);
  22.         // 追加分页参数设置
  23.         int from = (page - 1) * pageSize;
  24.         query.setFirstResult(from);// 设置起点,从0开始
  25.         query.setMaxResults(pageSize);// 设置页容量
  26.         List<Service> services = query.list();
  27.         for (Service service : services) {
  28.             System.out.println(
  29.                 service.getId() + " "
  30.                 + service.getUnixHost() + " "
  31.                 + service.getOsUserName());
  32.         }
  33.         session.close();
  34.     }
  35.     
  36. }

步骤二:测试

执行test3(),控制台输出结果如下图,输出了第1页的3条数据。

图-16

步骤三:编写查询总页数的方法

在TestHQL中,增加查询总页数的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10. // 其他查询方法略
  11.     /**
  12.      * 查询总页数
  13.      */
  14.     @Test
  15.     public void test4() {
  16.         int pageSize=3;
  17.         String hql = "select count(*) from Service";
  18.         Session session = HibernateUtil.getSession();
  19.         Query query = session.createQuery(hql);
  20.         int rows = Integer.parseInt(query.uniqueResult().toString());
  21.         int totalPages = 0;
  22.         if(rows%pageSize == 0) {
  23.             totalPages = rows/pageSize;
  24.         } else {
  25.             totalPages = rows/pageSize+1;
  26.         }
  27.         System.out.println(totalPages);
  28.         session.close();
  29.     }
  30.     
  31. }

步骤四:测试

执行test4(),控制台输出结果如下图,输出了总页数。

图-17

7.4 完整代码

以下为本案例的完整代码。

其中TestHQL完整代码如下:

代码

8 HQL查询,多表联合查询

8.1 问题

使用HQL,联合查询出业务账号与账务账号的数据。

8.2 方案

HQL中多表联合查询的方式有3种,分别是

  1. 对象方式关联
  2. join方式关联
  3. select子句关联

使用这三种方式来做业务账号与账务账号的联合查询。

8.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写对象方式关联的方法

在TestHQL中,增加对象方式关联的查询方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10.     // 其他查询方法略
  11.     
  12.     /**
  13.      * 多表联合查询-对象方式关联
  14.      */
  15.     @Test
  16.     public void test5() {
  17.         String hql = "select " +
  18.                 "s.id," +
  19.                 "s.osUserName," +
  20.                 "s.unixHost, " +
  21.                 "a.id,a.realName," +
  22.                 "a.idcardNo " +
  23.                 "from Service s,Account a " +
  24.                 "where s.account.id=a.id ";
  25.         Session session = HibernateUtil.getSession();
  26.         Query query = session.createQuery(hql);
  27.         List<Object[]> list = query.list();
  28.         for (Object[] objs : list) {
  29.             System.out.println(
  30.                     objs[0] + " " +
  31.                     objs[1] + " " +
  32.                     objs[2] + " " +
  33.                     objs[3] + " " +
  34.                     objs[4] + " " +
  35.                     objs[5]);
  36.         }
  37.         session.close();
  38.     }
  39.     
  40. }

步骤二:测试

执行test5(),控制台输出结果如下图,可以查询出关联的数据。

图-18

步骤三:编写join方式关联的方法

在TestHQL中,增加join方式关联的查询方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10.     // 其他查询方法略
  11.     
  12.     /**
  13.      * 多表联合查询-join方式关联
  14.      */
  15.     @Test
  16.     public void test6() {
  17.         String hql = "select " +
  18.                 "s.id,s.osUserName," +
  19.                 "s.unixHost, " +
  20.                 "a.id," +
  21.                 "a.realName," +
  22.                 "a.idcardNo " +
  23.                 "from Service s join s.account a ";
  24.         Session session = HibernateUtil.getSession();
  25.         Query query = session.createQuery(hql);
  26.         List<Object[]> list = query.list();
  27.         for (Object[] objs : list) {
  28.             System.out.println(
  29.                     objs[0] + " " +
  30.                     objs[1] + " " +
  31.                     objs[2] + " " +
  32.                     objs[3] + " " +
  33.                     objs[4] + " " +
  34.                     objs[5]);
  35.         }
  36.         session.close();
  37.     }
  38.     
  39. }

步骤四:测试

执行test6(),控制台输出结果如下图,可以查询出关联的数据。

图-19

步骤五:编写select子句关联的方法

在TestHQL中,增加select子句关联的查询方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10. // 其他查询方法略    
  11.     /**
  12.      * 多表联合查询-select子句关联
  13.      */
  14.     @Test
  15.     public void test7() {
  16.         String hql = "select " +
  17.                 "id," +
  18.                 "osUserName," +
  19.                 "unixHost, " +
  20.                 "account.id," +
  21.                 "account.realName," +
  22.                 "account.idcardNo " +
  23.                 "from Service ";
  24.         Session session = HibernateUtil.getSession();
  25.         Query query = session.createQuery(hql);
  26.         List<Object[]> list = query.list();
  27.         for (Object[] objs : list) {
  28.             System.out.println(
  29.                     objs[0] + " " +
  30.                     objs[1] + " " +
  31.                     objs[2] + " " +
  32.                     objs[3] + " " +
  33.                     objs[4] + " " +
  34.                     objs[5]);
  35.         }
  36.         session.close();
  37.     }
  38.     
  39. }

步骤六:测试

执行test7(),控制台输出结果如下,可以查询出关联的数据。

图-20

8.4 完整代码

以下为本案例的完整代码。

其中TestHQL完整代码如下:

代码

9 Hibernate中的SQL查询

9.1 问题

在Hibernate中直接使用SQL查询业务账号数据。

9.2 方案

Hibernate中可以通过SQLQuery对象来执行原始的SQL。

9.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写使用SQL查询的方法

在com.tarena.test包下,创建测试类TestOtherQuery,并在类中增加使用SQL查询的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.SQLQuery;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestOtherQuery {
  9.     
  10.     /**
  11.      * 使用SQL查询
  12.      */
  13.     @Test
  14.     public void test1() {
  15.         String sql = "select * " +
  16.                 "from SERVICE where unix_host=?";
  17.         Session session = HibernateUtil.getSession();
  18.         SQLQuery query = session.createSQLQuery(sql);
  19.         query.setString(0, "192.168.0.20");
  20.         query.addEntity(Service.class);
  21.         //采用Service类型封装一条数据
  22.         List<Service> list = query.list();
  23.         for(Service service : list){
  24.             System.out.println(
  25.                 service.getId() + " " +
  26.                 service.getOsUserName() + " " +
  27.                 service.getUnixHost() + " "
  28.             );
  29.         }
  30.         session.close();
  31.     }
  32.     
  33. }

步骤二:测试

执行test1(),控制台输出结果如下图,是直接执行SQL所查询到的内容:

图-21

9.4 完整代码

以下为本案例的完整代码。

其中TestOtherQuery完整代码如下:

代码

10 使用二级缓存

10.1 问题

在查询员工时,使用二级缓存,使得不同的session可以共享这些员工数据。

10.2 方案

使用ehcache缓存组件来支持二级缓存,二级缓存的使用步骤为

  1. 导入二级缓存驱动包
  2. 引入二级缓存配置文件
  3. 在hibernate.cfg.xml中开启二级缓存并指定二级缓存驱动类
  4. 在映射关系文件中设置缓存策略
  5. 执行查询

10.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:导入二级缓存驱动包

导入二级缓存驱动包ehcache-1.2.3.jar,导入后项目中包结构如下图:

图-22

步骤二:引入二级缓存配置文件

引入二级缓存配置文件ehcache.xml,并配置缓存参数,代码如下:

  1. <ehcache>
  2. <!--
  3.     缓存到硬盘时的缓存路径,java.io.tmpdir表示
  4.     系统默认缓存路径。
  5. -->
  6. <diskStore path="java.io.tmpdir"/>
  7. <!--
  8.     默认缓存配置。
  9.     maxElementsInMemory:
  10.         二级缓存可容纳最大对象数。
  11.     eternal:
  12.         是否保持二级缓存中对象不变。
  13.     timeToIdleSeconds:
  14.         允许对象空闲的时间,即对象最后一次访问起,超过该时间即失效。
  15.     timeToLiveSeconds:
  16.         允许对象存活的时间,即对象创建起,超过该时间即失效。
  17.     overflowToDisk:
  18.         内存不足时,是否允许使用硬盘缓存,写入路径参见diskStore。
  19. -->
  20. <defaultCache
  21. maxElementsInMemory="300"
  22. eternal="false"
  23. timeToIdleSeconds="120"
  24. timeToLiveSeconds="300"
  25. overflowToDisk="true"
  26. />
  27.     <!-- 自定义缓存配置 -->
  28. <cache name="myCache"
  29. maxElementsInMemory="2000"
  30. eternal="false"
  31. timeToIdleSeconds="300"
  32. timeToLiveSeconds="600"
  33. overflowToDisk="true"
  34. />
  35. </ehcache>

步骤三:开启二级缓存、指定二级缓存驱动类

在hibernate.cfg.xml中开启二级缓存,并指定二级缓存驱动类,代码如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE hibernate-configuration PUBLIC
  3.         "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  4.         "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
  5. <hibernate-configuration>
  6.     <session-factory>
  7.         <!-- 数据库连接信息,根据自己的数据库进行配置 -->
  8.         <property name="connection.url">
  9.             jdbc:oracle:thin:@localhost:1521:xe
  10.         </property>
  11.         <property name="connection.username">lhh</property>
  12.         <property name="connection.password">123456</property>
  13.         <property name="connection.driver_class">
  14.             oracle.jdbc.OracleDriver
  15.         </property>
  16.         
  17.         <!-- Hibernate配置信息 -->
  18.         <!-- dialect方言,用于配置生成针对哪个数据库的SQL语句 -->
  19.         <property name="dialect">
  20.             <!-- 方言类,Hibernate提供的,用于封装某种特定数据库的方言 -->
  21.             org.hibernate.dialect.OracleDialect
  22.         </property>
  23.         <!-- Hibernate生成的SQL是否输出到控制台 -->
  24.         <property name="show_sql">true</property>
  25.         <!-- 将SQL输出时是否格式化。为了方便截图,我将其设置为false -->
  26.         <property name="format_sql">false</property>
  27.         
  28.         <!-- 开启二级缓存 -->
  29.         <property name="hibernate.cache.use_second_level_cache">
  30.             true
  31.         </property>
  32.         <!-- 指定所采用的二级缓存驱动类 -->
  33.         <property name="hibernate.cache.provider_class">
  34.             org.hibernate.cache.EhCacheProvider
  35.         </property>        
  36.         
  37.         <!-- 声明映射关系文件 -->
  38.         <mapping resource="com/tarena/entity/Emp.hbm.xml" />
  39.         <mapping resource="com/tarena/entity/Account.hbm.xml" />
  40.         <mapping resource="com/tarena/entity/Service.hbm.xml" />        
  41.     </session-factory>
  42. </hibernate-configuration>

步骤四:设置缓存策略

在Emp.hbm.xml中,指定二级缓存策略为只读的,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4.     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  5. <hibernate-mapping>
  6.     <!-- 配置实体类和表的关系 -->
  7.     <class name="com.tarena.entity.Emp" table="emp">
  8.         <!--
  9.             开启二级缓存,指定二级缓存策略。
  10.             可以用region属性指定自定义的缓存设置。
  11.         -->
  12.         <cache usage="read-only" />
  13.         
  14.         <!-- 配置主键属性和字段的关系 -->
  15.         <id name="id" type="integer" column="id">
  16.             <!-- 用来指明主键的生成方式 -->
  17.             <generator class="sequence">
  18.                 <!-- 指定用于生成主键的sequence -->
  19.                 <param name="sequence">emp_seq</param>
  20.             </generator>
  21.         </id>
  22.         
  23.         <!-- 配置实体类中属性与表中字段的关系 -->
  24.         <property name="name"
  25.             type="string" column="name"/>
  26.         <property name="age"
  27.             type="integer" column="age"/>
  28.         <property name="salary"
  29.             type="double" column="salary"/>
  30.         <property name="birthday"
  31.             type="date" column="birthday"/>
  32.         <property name="lastLoginTime"
  33.             type="timestamp" column="last_login_time"/>
  34.         <property name="marry"
  35.             type="yes_no" column="marry"/>
  36.     </class>
  37. </hibernate-mapping>

步骤五:编写查询方法,测试二级缓存

在com.tarena.test包下,创建一个测试类TestSecondCache,增加一个测试二级缓存的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Emp;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestSecondCache {
  9.     /**
  10.      * 二级缓存
  11.      */
  12.     @Test
  13.     public void test1() {
  14.         Session session1 = HibernateUtil.getSession();
  15.         Emp e1 = (Emp) session1.get(Emp.class, 321);
  16.         System.out.println(e1.getName());
  17.         
  18.         System.out.println("-----------------");
  19.         
  20.         Session session2 = HibernateUtil.getSession();
  21.         Emp e2 = (Emp) session2.get(Emp.class, 321);
  22.         System.out.println(e2.getName());
  23.         
  24.         session1.close();
  25.         session2.close();
  26.     }
  27.     
  28. }

步骤六:测试

执行test1(),控制台输出结果如下图,可见第二次查询没有访问数据库,是二级缓存存在的缘故:

图-23

步骤七:管理二级缓存

二级缓存是SessionFactory级缓存,由它负责管理,因此需要获取到SessionFactory才能管理二级缓存,我们先在HibernateUtil中增加获取SessionFactory的方法,代码如下:

  1. package com.tarena.util;
  2. import org.hibernate.Session;
  3. import org.hibernate.SessionFactory;
  4. import org.hibernate.cfg.Configuration;
  5. public class HibernateUtil {
  6.     private static SessionFactory sessionFactory;
  7.     
  8.     static {
  9.         // 加载Hibernate主配置文件
  10.         Configuration conf = new Configuration();
  11.         conf.configure("/hibernate.cfg.xml");
  12.         sessionFactory = conf.buildSessionFactory();
  13.     }
  14.     /**
  15.      * 创建session
  16.      */
  17.     public static Session getSession() {
  18.         return sessionFactory.openSession();
  19.     }
  20.     
  21.     /**
  22.      * 返回SessionFactory
  23.      */
  24.     public static SessionFactory getSessionFactory() {
  25.         return sessionFactory;
  26.     }
  27.     
  28.     public static void main(String[] args) {
  29.         System.out.println(getSession());
  30.     }
  31. }

修改test1()方法,在第二次查询前清理二级缓存,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Emp;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestSecondCache {
  9.     /**
  10.      * 二级缓存
  11.      */
  12.     @Test
  13.     public void test1() {
  14.         Session session1 = HibernateUtil.getSession();
  15.         Emp e1 = (Emp) session1.get(Emp.class, 321);
  16.         System.out.println(e1.getName());
  17.         
  18.         System.out.println("-----------------");
  19.         HibernateUtil.getSessionFactory().evict(Emp.class);
  20.         
  21.         Session session2 = HibernateUtil.getSession();
  22.         Emp e2 = (Emp) session2.get(Emp.class, 321);
  23.         System.out.println(e2.getName());
  24.         
  25.         session1.close();
  26.         session2.close();
  27.     }
  28.     
  29. }

再次执行test1(),控制台输出结果如下图,由于清理了二级缓存,因此第二次查询时访问了数据库。

图-24

10.4 完整代码

以下是本案例的完整代码。

其中缓存配置文件ehcache.xml完整代码如下:

代码

hibernate.cfg.xml完整代码如下:

代码

emp.hbm.xml完整代码如下:

代码

TestSecondCache完整代码如下:

代码

11 使用查询缓存

11.1 问题

在查询多条员工数据时,使用查询缓存,缓存查询的HQL,使得再次使用同样HQL查询时不必重新访问数据库。

11.2 方案

查询缓存的使用步骤是

  1. 开启二级缓存
  2. 在hibernate.cfg.xml中开启查询缓存
  3. 在查询之前,开启查询缓存

11.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:开启二级缓存

开启二级缓存,由于上个案例已经完成,这一步就可以省略。但是要注意,查询缓存是基于二级缓存的,使用查询缓存的前提是开启二级缓存。

步骤二:开启查询缓存

在hibernate.cfg.xml中开启查询缓存,代码如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE hibernate-configuration PUBLIC
  3.         "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  4.         "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
  5. <hibernate-configuration>
  6.     <session-factory>
  7.         <!-- 数据库连接信息,根据自己的数据库进行配置 -->
  8.         <property name="connection.url">
  9.             jdbc:oracle:thin:@localhost:1521:xe
  10.         </property>
  11.         <property name="connection.username">lhh</property>
  12.         <property name="connection.password">123456</property>
  13.         <property name="connection.driver_class">
  14.             oracle.jdbc.OracleDriver
  15.         </property>
  16.         
  17.         <!-- Hibernate配置信息 -->
  18.         <!-- dialect方言,用于配置生成针对哪个数据库的SQL语句 -->
  19.         <property name="dialect">
  20.             <!-- 方言类,Hibernate提供的,用于封装某种特定数据库的方言 -->
  21.             org.hibernate.dialect.OracleDialect
  22.         </property>
  23.         <!-- Hibernate生成的SQL是否输出到控制台 -->
  24.         <property name="show_sql">true</property>
  25.         <!-- 将SQL输出时是否格式化。为了方便截图,我将其设置为false -->
  26.         <property name="format_sql">false</property>
  27.         
  28.         <!-- 开启二级缓存 -->
  29.         <property name="hibernate.cache.use_second_level_cache">
  30.             true
  31.         </property>
  32.         <!-- 指定所采用的二级缓存驱动 -->
  33.         <property name="hibernate.cache.provider_class">
  34.             org.hibernate.cache.EhCacheProvider
  35.         </property>        
  36.         <!-- 开启查询缓存 -->
  37.         <property name="hibernate.cache.use_query_cache">
  38.             true
  39.         </property>
  40.                 
  41.         <!-- 声明映射关系文件 -->
  42.         <mapping resource="com/tarena/entity/Emp.hbm.xml" />
  43.         <mapping resource="com/tarena/entity/Account.hbm.xml" />
  44.         <mapping resource="com/tarena/entity/Service.hbm.xml" />        
  45.     </session-factory>
  46. </hibernate-configuration>

步骤三:查询前开启查询缓存

在TestSecondCache中,增加测试查询缓存的方法,使用相同的HQL执行2次查询,每次查询前都设置开启查询缓存,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Emp;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestSecondCache {
  9.     /**
  10.      * 二级缓存
  11.      */
  12.     @Test
  13.     public void test1() {
  14.         Session session1 = HibernateUtil.getSession();
  15.         Emp e1 = (Emp) session1.get(Emp.class, 321);
  16.         System.out.println(e1.getName());
  17.         
  18.         System.out.println("-----------------");
  19.         HibernateUtil.getSessionFactory().evict(Emp.class);
  20.         
  21.         Session session2 = HibernateUtil.getSession();
  22.         Emp e2 = (Emp) session2.get(Emp.class, 321);
  23.         System.out.println(e2.getName());
  24.         
  25.         session1.close();
  26.         session2.close();
  27.     }
  28.     
  29.     /**
  30.      * 查询缓存
  31.      */
  32.     @Test
  33.     public void test2() {
  34.         Session session = HibernateUtil.getSession();
  35.         
  36.         String hql = "from Emp";
  37.         Query query = session.createQuery(hql);
  38.         // 开启查询缓存
  39.         query.setCacheable(true);
  40.         List<Emp> emps = query.list();
  41.         for(Emp e : emps) {
  42.             System.out.println(e.getId() + " " + e.getName());
  43.         }
  44.         
  45.         System.out.println("---------------");
  46.         
  47.         hql = "from Emp";
  48.         query = session.createQuery(hql);
  49.         // 开启查询缓存
  50.         query.setCacheable(true);
  51.         emps = query.list();
  52.         for(Emp e : emps) {
  53.             System.out.println(e.getId() + " " + e.getName());
  54.         }
  55.         
  56.         session.close();
  57.     }
  58.     
  59. }

步骤四:测试

执行test2(),控制台输出结果如下图,可见在使用相同HQL查询时,第二次查询不必再次访问数据库。

图-25

步骤五:管理查询缓存

修改test2(),在第二次查询之前清理查询缓存,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Emp;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestSecondCache {
  9.     /**
  10.      * 二级缓存
  11.      */
  12.     @Test
  13.     public void test1() {
  14.         Session session1 = HibernateUtil.getSession();
  15.         Emp e1 = (Emp) session1.get(Emp.class, 321);
  16.         System.out.println(e1.getName());
  17.         
  18.         System.out.println("-----------------");
  19.         HibernateUtil.getSessionFactory().evict(Emp.class);
  20.         
  21.         Session session2 = HibernateUtil.getSession();
  22.         Emp e2 = (Emp) session2.get(Emp.class, 321);
  23.         System.out.println(e2.getName());
  24.         
  25.         session1.close();
  26.         session2.close();
  27.     }
  28.     
  29.     /**
  30.      * 查询缓存
  31.      */
  32.     @Test
  33.     public void test2() {
  34.         Session session = HibernateUtil.getSession();
  35.         
  36.         String hql = "from Emp";
  37.         Query query = session.createQuery(hql);
  38.         // 开启查询缓存
  39.         query.setCacheable(true);
  40.         List<Emp> emps = query.list();
  41.         for(Emp e : emps) {
  42.             System.out.println(e.getId() + " " + e.getName());
  43.         }
  44.         
  45.         System.out.println("---------------");
  46.         HibernateUtil.getSessionFactory().evictQueries();
  47.         
  48.         hql = "from Emp";
  49.         query = session.createQuery(hql);
  50.         // 开启查询缓存
  51.         query.setCacheable(true);
  52.         emps = query.list();
  53.         for(Emp e : emps) {
  54.             System.out.println(e.getId() + " " + e.getName());
  55.         }
  56.         
  57.         session.close();
  58.     }
  59.     
  60. }

再次执行test2(),控制台输出结果如下图,可见第二次查询也访问了数据库,主要是清理了查询缓存中数据的缘故。

图-26

11.4 完整代码

以下为本案例的完整代码。

其中Hibernate.cfg.xml完整代码如下:

代码

TestSecondCache完整代码如下:

代码
Top

JAVA Hibernate DAY03

  1. 使用多对一关联映射
  2. 关联查询的一些特性
  3. 级联添加/修改
  4. 级联删除
  5. HQL查询,按条件查询
  6. HQL查询,查询一部分字段
  7. HQL查询,分页查询
  8. HQL查询,多表联合查询
  9. Hibernate中的SQL查询
  10. 使用二级缓存
  11. 使用查询缓存

1 使用多对一关联映射

1.1 问题

使用多对一关联映射,在查询业务账号时,自动查询出它对应的账务账号。

1.2 方案

多对一关联映射开发步骤:

  1. 业务账号与账务账号具有多对一关系,他们的关系字段是service.account_id。
  2. 在业务账号中追加Account类型的属性,用于封装它对应的唯一账务账号。
  3. 在业务账号映射关系文件中配置此属性。

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:在业务账号实体类中追加属性

在业务账号实体类Service中,追加Account类型的属性,用于封装它对应的唯一账务账号。由于这个属性包含了账务账号的ID,因此accountId属性可以去掉了,实际上这个属性必须去掉,否则会报错。代码如下:

  1. package com.tarena.entity;
  2. import java.sql.Date;
  3. public class Service {
  4.     private Integer id;
  5. //    private Integer accountId;
  6.     private String unixHost;
  7.     private String osUserName;
  8.     private String loginPassword;
  9.     private String status;
  10.     private Date createDate;
  11.     private Date pauseDate;
  12.     private Date closeDate;
  13.     private Integer costId;
  14.     // 追加属性,用于封装对应的Account记录
  15.     private Account account;
  16.     public Integer getId() {
  17.         return id;
  18.     }
  19.     public void setId(Integer id) {
  20.         this.id = id;
  21.     }
  22. //    public Integer getAccountId() {
  23. //        return accountId;
  24. //    }
  25. //
  26. //    public void setAccountId(Integer accountId) {
  27. //        this.accountId = accountId;
  28. //    }
  29.     public Account getAccount() {
  30.         return account;
  31.     }
  32.     public void setAccount(Account account) {
  33.         this.account = account;
  34.     }
  35.     public String getUnixHost() {
  36.         return unixHost;
  37.     }
  38.     public void setUnixHost(String unixHost) {
  39.         this.unixHost = unixHost;
  40.     }
  41.     public String getOsUserName() {
  42.         return osUserName;
  43.     }
  44.     public void setOsUserName(String osUserName) {
  45.         this.osUserName = osUserName;
  46.     }
  47.     public String getLoginPassword() {
  48.         return loginPassword;
  49.     }
  50.     public void setLoginPassword(String loginPassword) {
  51.         this.loginPassword = loginPassword;
  52.     }
  53.     public String getStatus() {
  54.         return status;
  55.     }
  56.     public void setStatus(String status) {
  57.         this.status = status;
  58.     }
  59.     public Date getCreateDate() {
  60.         return createDate;
  61.     }
  62.     public void setCreateDate(Date createDate) {
  63.         this.createDate = createDate;
  64.     }
  65.     public Date getPauseDate() {
  66.         return pauseDate;
  67.     }
  68.     public void setPauseDate(Date pauseDate) {
  69.         this.pauseDate = pauseDate;
  70.     }
  71.     public Date getCloseDate() {
  72.         return closeDate;
  73.     }
  74.     public void setCloseDate(Date closeDate) {
  75.         this.closeDate = closeDate;
  76.     }
  77.     public Integer getCostId() {
  78.         return costId;
  79.     }
  80.     public void setCostId(Integer costId) {
  81.         this.costId = costId;
  82.     }
  83. }

步骤二:在业务账号映射关系文件中配置这个属性

在业务账号映射关系文件service.hbm.xml中,配置这个关联属性,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Service" table="SERVICE">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">SERVICE_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <!--
  12.             由于account属性已经体现了业务账号与账务账号的关系,
  13.             并且account属性可以包含账务账号ID,因此accountId可以去掉,
  14.             实际上这里必须去掉这个属性的配置,否则会报错。
  15.         -->
  16.         <!-- <property name="accountId"
  17.             type="integer" column="ACCOUNT_ID"/> -->
  18.         <property name="unixHost"
  19.             type="string" column="UNIX_HOST"/>
  20.         <property name="osUserName"
  21.             type="string" column="OS_USERNAME"/>
  22.         <property name="loginPassword"
  23.             type="string" column="LOGIN_PASSWD"/>
  24.         <property name="status"
  25.             type="string" column="STATUS"/>
  26.         <property name="createDate"
  27.             type="date" column="CREATE_DATE"/>
  28.         <property name="pauseDate"
  29.             type="date" column="PAUSE_DATE"/>
  30.         <property name="closeDate"
  31.             type="date" column="CLOSE_DATE"/>
  32.         <property name="costId"
  33.             type="integer" column="COST_ID"/>
  34.             
  35.         <!-- 配置account属性,采用多对一关系加载相关的account内容 -->
  36.         <many-to-one name="account" column="ACCOUNT_ID"
  37.             class="com.tarena.entity.Account"/>            
  38.     </class>
  39. </hibernate-mapping>

步骤三:创建测试类

在com.tarena.test包下,创建一个测试类TestManyToOne,并且增加一个测试方法。在方法中查询出一条业务账号数据,然后输出这个业务账号的一些属性,同时输出account属性的值。代码如下:

  1. package com.tarena.test;
  2. import org.hibernate.Session;
  3. import org.junit.Test;
  4. import com.tarena.entity.Service;
  5. import com.tarena.util.HibernateUtil;
  6. public class TestManyToOne {
  7.     
  8.     @Test
  9.     public void test1() {
  10.         Session session = HibernateUtil.getSession();
  11.         Service service =
  12.             (Service) session.get(Service.class, 2001);
  13.         System.out.println(service.getOsUserName());
  14.         System.out.println("------------------");
  15.         System.out.println(
  16.                 service.getAccount().getId() + " " +
  17.                 service.getAccount().getIdcardNo());
  18.     }
  19.     
  20. }

步骤四:测试

执行这个测试方法,控制台输出结果如下图,可以看出在查询SERVICE的同时,Hibernate自动查询出了它对应的ACCOUNT数据,并且这个关联查询是采用延迟加载机制实现的。

图-1

1.4 完整代码

以下为本案例的完整代码。

其中业务账号实体类完整代码如下:

代码

业务账号映射关系文件代码如下:

代码

测试类代码如下:

代码

2 关联查询的一些特性

2.1 问题

请按照如下的方式使用关联映射:

  1. 不采用延迟加载的方式查询关联属性。
  2. 采用关联查询一次性查出2张表的数据。

2.2 方案

可以按照如下的方式实现上述问题的要求:

  1. 通过lazy=”false”取消延迟加载。
  2. 通过fetch=”join”设置关联查询。

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建项目

复制项目HibernateDay02,粘贴并修改项目名为HibernateDay03。

步骤二:将一对多关联映射取消延迟加载

修改HibernateDay03项目中的Account.hbm.xml文件,将services属性的配置追加lazy=”false”,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Account" table="ACCOUNT">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">ACCOUNT_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <property name="recommenderId"
  12.             type="integer" column="RECOMMENDER_ID"/>
  13.         <property name="loginName"
  14.             type="string" column="LOGIN_NAME"/>
  15.         <property name="loginPassword"
  16.             type="string" column="LOGIN_PASSWD"/>
  17.         <property name="status"
  18.             type="string" column="STATUS"/>
  19.         <property name="createDate"
  20.             type="date" column="CREATE_DATE"/>
  21.         <property name="pauseDate"
  22.             type="date" column="PAUSE_DATE"/>
  23.         <property name="closeDate"
  24.             type="date" column="CLOSE_DATE"/>
  25.         <property name="realName"
  26.             type="string" column="REAL_NAME"/>
  27.         <property name="idcardNo"
  28.             type="string" column="IDCARD_NO"/>
  29.         <property name="birthdate"
  30.             type="date" column="BIRTHDATE"/>
  31.         <property name="gender"
  32.             type="string" column="GENDER"/>
  33.         <property name="occupation"
  34.             type="string" column="OCCUPATION"/>
  35.         <property name="telephone"
  36.             type="string" column="TELEPHONE"/>
  37.         <property name="email"
  38.             type="string" column="EMAIL"/>
  39.         <property name="mailaddress"
  40.             type="string" column="MAILADDRESS"/>
  41.         <property name="zipcode"
  42.             type="string" column="ZIPCODE"/>
  43.         <property name="qq"
  44.             type="string" column="QQ"/>
  45.         <property name="lastLoginTime"
  46.             type="date" column="LAST_LOGIN_TIME"/>
  47.         <property name="lastLoginIp"
  48.             type="string" column="LAST_LOGIN_IP"/>
  49.             
  50.         <!-- 配置services属性,采用一对多的关系 -->
  51.         <set name="services" lazy="false">
  52.             <!-- 用于指定关联条件,写关联条件的外键字段 -->
  53.             <key column="ACCOUNT_ID"/>
  54.             <!-- 用于指定采用哪种关系,加载哪方数据 -->
  55.             <one-to-many class="com.tarena.entity.Service"/>
  56.         </set>
  57.     </class>
  58. </hibernate-mapping>

执行TestOneToMany中的测试方法,控制台输出结果如下图,可以看出在查询账务账号之后Hibernate立刻查询了它对应的业务账号,已经取消了延迟加载。

图-2

步骤三:将多对一关联映射取消延迟加载

修改Service.hbm.xml,将account属性的配置追加lazy=”false”,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Service" table="SERVICE">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">SERVICE_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <!--
  12.             由于account属性已经体现了业务账号与账务账号的关系,
  13.             并且account属性可以包含账务账号ID,因此accountId可以去掉,
  14.             实际上这里必须去掉这个属性的配置,否则会报错。
  15.         -->
  16.         <!-- <property name="accountId"
  17.             type="integer" column="ACCOUNT_ID"/> -->
  18.         <property name="unixHost"
  19.             type="string" column="UNIX_HOST"/>
  20.         <property name="osUserName"
  21.             type="string" column="OS_USERNAME"/>
  22.         <property name="loginPassword"
  23.             type="string" column="LOGIN_PASSWD"/>
  24.         <property name="status"
  25.             type="string" column="STATUS"/>
  26.         <property name="createDate"
  27.             type="date" column="CREATE_DATE"/>
  28.         <property name="pauseDate"
  29.             type="date" column="PAUSE_DATE"/>
  30.         <property name="closeDate"
  31.             type="date" column="CLOSE_DATE"/>
  32.         <property name="costId"
  33.             type="integer" column="COST_ID"/>
  34.             
  35.         <!-- 配置account属性,采用多对一关系加载相关的account内容 -->
  36.         <many-to-one name="account" column="ACCOUNT_ID"
  37.             class="com.tarena.entity.Account"
  38.             lazy="false"/>            
  39.     </class>
  40. </hibernate-mapping>

执行TestManyToOne中的测试方法,控制台输出效果如下图,可以看出在查询业务账号之后Hibernate立刻查询了对应的账务账号,取消了延迟加载。

图-3

步骤四:将一对多关联映射设置为join方式抓取数据

修改Account.hbm.xml,将services属性的配置追加fetch=“join”,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Account" table="ACCOUNT">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">ACCOUNT_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <property name="recommenderId"
  12.             type="integer" column="RECOMMENDER_ID"/>
  13.         <property name="loginName"
  14.             type="string" column="LOGIN_NAME"/>
  15.         <property name="loginPassword"
  16.             type="string" column="LOGIN_PASSWD"/>
  17.         <property name="status"
  18.             type="string" column="STATUS"/>
  19.         <property name="createDate"
  20.             type="date" column="CREATE_DATE"/>
  21.         <property name="pauseDate"
  22.             type="date" column="PAUSE_DATE"/>
  23.         <property name="closeDate"
  24.             type="date" column="CLOSE_DATE"/>
  25.         <property name="realName"
  26.             type="string" column="REAL_NAME"/>
  27.         <property name="idcardNo"
  28.             type="string" column="IDCARD_NO"/>
  29.         <property name="birthdate"
  30.             type="date" column="BIRTHDATE"/>
  31.         <property name="gender"
  32.             type="string" column="GENDER"/>
  33.         <property name="occupation"
  34.             type="string" column="OCCUPATION"/>
  35.         <property name="telephone"
  36.             type="string" column="TELEPHONE"/>
  37.         <property name="email"
  38.             type="string" column="EMAIL"/>
  39.         <property name="mailaddress"
  40.             type="string" column="MAILADDRESS"/>
  41.         <property name="zipcode"
  42.             type="string" column="ZIPCODE"/>
  43.         <property name="qq"
  44.             type="string" column="QQ"/>
  45.         <property name="lastLoginTime"
  46.             type="date" column="LAST_LOGIN_TIME"/>
  47.         <property name="lastLoginIp"
  48.             type="string" column="LAST_LOGIN_IP"/>
  49.             
  50.         <!-- 配置services属性,采用一对多的关系 -->
  51.         <set name="services" lazy="false" fetch="join">
  52.             <!-- 用于指定关联条件,写关联条件的外键字段 -->
  53.             <key column="ACCOUNT_ID"/>
  54.             <!-- 用于指定采用哪种关系,加载哪方数据 -->
  55.             <one-to-many class="com.tarena.entity.Service"/>
  56.         </set>
  57.     </class>
  58. </hibernate-mapping>

执行TestOneToMany中的测试方法,控制台输出结果如下图,可以看出查询时采用了join方式查询。

图-4

步骤五:将多对一关联映射设置为join方式抓取数据

修改Service.hbm.xml,将account属性追加fetch=“join”,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Service" table="SERVICE">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">SERVICE_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <!--
  12.             由于account属性已经体现了业务账号与账务账号的关系,
  13.             并且account属性可以包含账务账号ID,因此accountId可以去掉,
  14.             实际上这里必须去掉这个属性的配置,否则会报错。
  15.         -->
  16.         <!-- <property name="accountId"
  17.             type="integer" column="ACCOUNT_ID"/> -->
  18.         <property name="unixHost"
  19.             type="string" column="UNIX_HOST"/>
  20.         <property name="osUserName"
  21.             type="string" column="OS_USERNAME"/>
  22.         <property name="loginPassword"
  23.             type="string" column="LOGIN_PASSWD"/>
  24.         <property name="status"
  25.             type="string" column="STATUS"/>
  26.         <property name="createDate"
  27.             type="date" column="CREATE_DATE"/>
  28.         <property name="pauseDate"
  29.             type="date" column="PAUSE_DATE"/>
  30.         <property name="closeDate"
  31.             type="date" column="CLOSE_DATE"/>
  32.         <property name="costId"
  33.             type="integer" column="COST_ID"/>
  34.             
  35.         <!-- 配置account属性,采用多对一关系加载相关的account内容 -->
  36.         <many-to-one name="account" column="ACCOUNT_ID"
  37.             class="com.tarena.entity.Account"
  38.             lazy="false" fetch="join"/>            
  39.     </class>
  40. </hibernate-mapping>

执行TestManyToOne中的测试方法,控制台输出结果如下图,可以看出查询时采用了join方式查询。

图-5

2.4 完整代码

以下为本案例的完整代码。

其中账务账号映射关系文件完整代码如下:

代码

业务账号映射关系文件完整代码如下:

代码

3 级联添加/修改

3.1 问题

使用级联添加/修改,在添加/修改账务账号时,自动添加/修改其对应的业务账号。

3.2 方案

通过在映射关系文件中设置cascade=”save-update”,可以支持级联添加/修改。

3.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:在账务账号映射关系文件中设置级联添加/修改

在账务账号映射关系文件Account.hbm.xml中,通过cascade=“save-update”设置支持级联添加/修改,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Account" table="ACCOUNT">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">ACCOUNT_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <property name="recommenderId"
  12.             type="integer" column="RECOMMENDER_ID"/>
  13.         <property name="loginName"
  14.             type="string" column="LOGIN_NAME"/>
  15.         <property name="loginPassword"
  16.             type="string" column="LOGIN_PASSWD"/>
  17.         <property name="status"
  18.             type="string" column="STATUS"/>
  19.         <property name="createDate"
  20.             type="date" column="CREATE_DATE"/>
  21.         <property name="pauseDate"
  22.             type="date" column="PAUSE_DATE"/>
  23.         <property name="closeDate"
  24.             type="date" column="CLOSE_DATE"/>
  25.         <property name="realName"
  26.             type="string" column="REAL_NAME"/>
  27.         <property name="idcardNo"
  28.             type="string" column="IDCARD_NO"/>
  29.         <property name="birthdate"
  30.             type="date" column="BIRTHDATE"/>
  31.         <property name="gender"
  32.             type="string" column="GENDER"/>
  33.         <property name="occupation"
  34.             type="string" column="OCCUPATION"/>
  35.         <property name="telephone"
  36.             type="string" column="TELEPHONE"/>
  37.         <property name="email"
  38.             type="string" column="EMAIL"/>
  39.         <property name="mailaddress"
  40.             type="string" column="MAILADDRESS"/>
  41.         <property name="zipcode"
  42.             type="string" column="ZIPCODE"/>
  43.         <property name="qq"
  44.             type="string" column="QQ"/>
  45.         <property name="lastLoginTime"
  46.             type="date" column="LAST_LOGIN_TIME"/>
  47.         <property name="lastLoginIp"
  48.             type="string" column="LAST_LOGIN_IP"/>
  49.             
  50.         <!-- 配置services属性,采用一对多的关系 -->
  51.         <set name="services"
  52.             lazy="false" fetch="join"
  53.             cascade="save-update">
  54.             <!-- 用于指定关联条件,写关联条件的外键字段 -->
  55.             <key column="ACCOUNT_ID"/>
  56.             <!-- 用于指定采用哪种关系,加载哪方数据 -->
  57.             <one-to-many
  58.                 class="com.tarena.entity.Service"/>
  59.         </set>
  60.     </class>
  61. </hibernate-mapping>

步骤二:测试级联添加

在com.tarena.test包下,创建测试类TestCascade,并在类中增加测试级联添加的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.HashSet;
  3. import java.util.Set;
  4. import org.hibernate.HibernateException;
  5. import org.hibernate.Session;
  6. import org.hibernate.Transaction;
  7. import org.junit.Test;
  8. import com.tarena.entity.Account;
  9. import com.tarena.entity.Service;
  10. import com.tarena.util.HibernateUtil;
  11. public class TestCascade {
  12.     /**
  13.      * 级联添加
  14.      */
  15.     @Test
  16.     public void test1() {
  17.         // 模拟要添加的账务账号
  18.         Account a = new Account();
  19.         a.setLoginName("gg");
  20.         a.setLoginPassword("123");
  21.         a.setRealName("gg");
  22.         a.setIdcardNo("120392198410282549");
  23.         a.setStatus("0");
  24.         a.setTelephone("110");
  25.         // 模拟要添加的业务账号
  26.         Service s1 = new Service();
  27.         s1.setAccount(a);
  28.         s1.setOsUserName("gg1");
  29.         s1.setLoginPassword("123");
  30.         s1.setUnixHost("192.168.1.1");
  31.         s1.setCostId(5);
  32.         s1.setStatus("0");
  33.         Service s2 = new Service();
  34.         s2.setAccount(a);
  35.         s2.setOsUserName("gg2");
  36.         s2.setLoginPassword("123");
  37.         s2.setUnixHost("192.168.1.2");
  38.         s2.setCostId(5);
  39.         s2.setStatus("0");
  40.         // 将业务账号与账务账号关联
  41.         a.setServices(new HashSet<Service>());
  42.         a.getServices().add(s1);
  43.         a.getServices().add(s2);
  44.         Session session = HibernateUtil.getSession();
  45.         Transaction ts = session.beginTransaction();
  46.         try {
  47.             session.save(a);
  48.             ts.commit();
  49.         } catch (HibernateException e) {
  50.             e.printStackTrace();
  51.             ts.rollback();
  52.         } finally {
  53.             session.close();
  54.         }
  55.     }
  56.     
  57. }

执行test1()方法,控制台输出结果如下图,可以看出在新增账务账号之后,Hibernate自动新增了账务账号对应的业务账号数据,这就是级联添加所起到的作用。

图-6

此时,查询账务账号表,数据如下图,其中id=380的行就是刚刚添加的行。

图-7

再查询业务账号表,数据如下图,其中account_id=380的行就是级联添加的行。

图-8

步骤三:测试级联修改

在TestCascade测试类中,增加测试级联修改的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.HashSet;
  3. import java.util.Set;
  4. import org.hibernate.HibernateException;
  5. import org.hibernate.Session;
  6. import org.hibernate.Transaction;
  7. import org.junit.Test;
  8. import com.tarena.entity.Account;
  9. import com.tarena.entity.Service;
  10. import com.tarena.util.HibernateUtil;
  11. public class TestCascade {
  12.     /**
  13.      * 级联添加
  14.      */
  15.     @Test
  16.     public void test1() {
  17.         // 模拟要添加的账务账号
  18.         Account a = new Account();
  19.         a.setLoginName("gg");
  20.         a.setLoginPassword("123");
  21.         a.setRealName("gg");
  22.         a.setIdcardNo("120392198410282549");
  23.         a.setStatus("0");
  24.         a.setTelephone("110");
  25.         // 模拟要添加的业务账号
  26.         Service s1 = new Service();
  27.         s1.setAccount(a);
  28.         s1.setOsUserName("gg1");
  29.         s1.setLoginPassword("123");
  30.         s1.setUnixHost("192.168.1.1");
  31.         s1.setCostId(5);
  32.         s1.setStatus("0");
  33.         Service s2 = new Service();
  34.         s2.setAccount(a);
  35.         s2.setOsUserName("gg2");
  36.         s2.setLoginPassword("123");
  37.         s2.setUnixHost("192.168.1.2");
  38.         s2.setCostId(5);
  39.         s2.setStatus("0");
  40.         // 将业务账号与账务账号关联
  41.         a.setServices(new HashSet<Service>());
  42.         a.getServices().add(s1);
  43.         a.getServices().add(s2);
  44.         Session session = HibernateUtil.getSession();
  45.         Transaction ts = session.beginTransaction();
  46.         try {
  47.             session.save(a);
  48.             ts.commit();
  49.         } catch (HibernateException e) {
  50.             e.printStackTrace();
  51.             ts.rollback();
  52.         } finally {
  53.             session.close();
  54.         }
  55.     }
  56.     /**
  57.      * 级联修改
  58.      */
  59.     @Test
  60.     public void test2() {
  61.         Session session = HibernateUtil.getSession();
  62.         // 查询出要修改的账务账号
  63.         Account account =
  64. #cold_bold            (Account) session.get(Account.class, 380);
  65.         // 模拟对账务账号的修改
  66.         account.setLoginName("pp");
  67.         Set<Service> services = account.getServices();
  68.         for (Service service : services) {
  69.             // 模拟对业务账号的修改
  70.             service.setLoginPassword("pp");
  71.         }
  72.         Transaction ts = session.beginTransaction();
  73.         try {
  74.             session.update(account);
  75.             ts.commit();
  76.         } catch (HibernateException e) {
  77.             e.printStackTrace();
  78.             ts.rollback();
  79.         } finally {
  80.             session.close();
  81.         }
  82.     }
  83.     
  84. }

执行test2()方法后,控制台输出的结果如下图,可以看出在修改完账务账号之后,Hibernate自动修改了它对应的业务账号数据。

图-9

此时,查询账务账号表,id=380的行已经发生了改变。

图-10

再查询业务账号表,account_id=380的行也发生了改变。

图-11

3.4 完整代码

以下为本案例的完整代码。

其中Account.hbm.xml完整代码如下:

代码

TestCascade完整代码如下:

代码

4 级联删除

4.1 问题

使用级联删除,在删除账务账号时,自动删除其对应的业务账号。

4.2 方案

通过在映射关系文件中设置cascade=“delete”,可以支持级联删除,如果想全面支持级联添加、修改和删除,可以设置cascade=“all”。

4.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:在账务账号映射关系文件中设置级联删除

在账务账号映射关系文件Account.hbm.xml中,通过cascade=“all”设置支持级联删除,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Account" table="ACCOUNT">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">ACCOUNT_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <property name="recommenderId"
  12.             type="integer" column="RECOMMENDER_ID"/>
  13.         <property name="loginName"
  14.             type="string" column="LOGIN_NAME"/>
  15.         <property name="loginPassword"
  16.             type="string" column="LOGIN_PASSWD"/>
  17.         <property name="status"
  18.             type="string" column="STATUS"/>
  19.         <property name="createDate"
  20.             type="date" column="CREATE_DATE"/>
  21.         <property name="pauseDate"
  22.             type="date" column="PAUSE_DATE"/>
  23.         <property name="closeDate"
  24.             type="date" column="CLOSE_DATE"/>
  25.         <property name="realName"
  26.             type="string" column="REAL_NAME"/>
  27.         <property name="idcardNo"
  28.             type="string" column="IDCARD_NO"/>
  29.         <property name="birthdate"
  30.             type="date" column="BIRTHDATE"/>
  31.         <property name="gender"
  32.             type="string" column="GENDER"/>
  33.         <property name="occupation"
  34.             type="string" column="OCCUPATION"/>
  35.         <property name="telephone"
  36.             type="string" column="TELEPHONE"/>
  37.         <property name="email"
  38.             type="string" column="EMAIL"/>
  39.         <property name="mailaddress"
  40.             type="string" column="MAILADDRESS"/>
  41.         <property name="zipcode"
  42.             type="string" column="ZIPCODE"/>
  43.         <property name="qq"
  44.             type="string" column="QQ"/>
  45.         <property name="lastLoginTime"
  46.             type="date" column="LAST_LOGIN_TIME"/>
  47.         <property name="lastLoginIp"
  48.             type="string" column="LAST_LOGIN_IP"/>
  49.             
  50.         <!-- 配置services属性,采用一对多的关系 -->
  51.         <set name="services"
  52.             lazy="false" fetch="join"
  53.             cascade="all">
  54.             <!-- 用于指定关联条件,写关联条件的外键字段 -->
  55.             <key column="ACCOUNT_ID"/>
  56.             <!-- 用于指定采用哪种关系,加载哪方数据 -->
  57.             <one-to-many
  58.                 class="com.tarena.entity.Service"/>
  59.         </set>
  60.     </class>
  61. </hibernate-mapping>

步骤二:测试级联删除

在TestCascade测试类中,增加测试级联删除的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.HashSet;
  3. import java.util.Set;
  4. import org.hibernate.HibernateException;
  5. import org.hibernate.Session;
  6. import org.hibernate.Transaction;
  7. import org.junit.Test;
  8. import com.tarena.entity.Account;
  9. import com.tarena.entity.Service;
  10. import com.tarena.util.HibernateUtil;
  11. public class TestCascade {
  12.     /**
  13.      * 级联添加
  14.      */
  15.     @Test
  16.     public void test1() {
  17.         // 模拟要添加的账务账号
  18.         Account a = new Account();
  19.         a.setLoginName("gg");
  20.         a.setLoginPassword("123");
  21.         a.setRealName("gg");
  22.         a.setIdcardNo("120392198410282549");
  23.         a.setStatus("0");
  24.         a.setTelephone("110");
  25.         // 模拟要添加的业务账号
  26.         Service s1 = new Service();
  27.         s1.setAccount(a);
  28.         s1.setOsUserName("gg1");
  29.         s1.setLoginPassword("123");
  30.         s1.setUnixHost("192.168.1.1");
  31.         s1.setCostId(5);
  32.         s1.setStatus("0");
  33.         Service s2 = new Service();
  34.         s2.setAccount(a);
  35.         s2.setOsUserName("gg2");
  36.         s2.setLoginPassword("123");
  37.         s2.setUnixHost("192.168.1.2");
  38.         s2.setCostId(5);
  39.         s2.setStatus("0");
  40.         // 将业务账号与账务账号关联
  41.         a.setServices(new HashSet<Service>());
  42.         a.getServices().add(s1);
  43.         a.getServices().add(s2);
  44.         Session session = HibernateUtil.getSession();
  45.         Transaction ts = session.beginTransaction();
  46.         try {
  47.             session.save(a);
  48.             ts.commit();
  49.         } catch (HibernateException e) {
  50.             e.printStackTrace();
  51.             ts.rollback();
  52.         } finally {
  53.             session.close();
  54.         }
  55.     }
  56.     /**
  57.      * 级联修改
  58.      */
  59.     @Test
  60.     public void test2() {
  61.         Session session = HibernateUtil.getSession();
  62.         // 查询出要修改的账务账号
  63.         Account account =
  64.             (Account) session.get(Account.class, 380);
  65.         // 模拟对账务账号的修改
  66.         account.setLoginName("pp");
  67.         Set<Service> services = account.getServices();
  68.         for (Service service : services) {
  69.             // 模拟对业务账号的修改
  70.             service.setLoginPassword("pp");
  71.         }
  72.         Transaction ts = session.beginTransaction();
  73.         try {
  74.             session.update(account);
  75.             ts.commit();
  76.         } catch (HibernateException e) {
  77.             e.printStackTrace();
  78.             ts.rollback();
  79.         } finally {
  80.             session.close();
  81.         }
  82.     }
  83.     /**
  84.      * 级联删除
  85.      */
  86.     @Test
  87.     public void test3() {
  88.         Session session = HibernateUtil.getSession();
  89.         Account account = (Account) session.get(Account.class, 380);
  90.         Transaction ts = session.beginTransaction();
  91.         try {
  92.             session.delete(account);
  93.             ts.commit();
  94.         } catch (HibernateException e) {
  95.             e.printStackTrace();
  96.             ts.rollback();
  97.         } finally {
  98.             session.close();
  99.         }
  100.     }
  101.     
  102. }

步骤三:测试

执行test3()方法,控制台输出结果如下图,可以看出程序在运行时报错了:

图-12

步骤四:排错

上述错误是执行第二个SQL时产生的,这个SQL的目的是维护关系字段,将其置为null,而这个外键字段存在非空约束,因此报错。类似的事情在级联添加时也看到过,参考图-6,不同的是级联添加时要将关联字段设置为新生成的账务账号ID。然而,在级联新增或删除时业务账号时,业务账号本身已经维护好了关联字段,因此这个额外的操作是多余的,可以去掉,我们可以在关联属性上通过inverse=“true“去掉这个行为,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <hibernate-mapping>
  5.     <class name="com.tarena.entity.Account" table="ACCOUNT">
  6.         <id name="id" type="integer" column="id">
  7.             <generator class="sequence">
  8.                 <param name="sequence">ACCOUNT_SEQ</param>
  9.             </generator>
  10.         </id>
  11.         <property name="recommenderId"
  12.             type="integer" column="RECOMMENDER_ID"/>
  13.         <property name="loginName"
  14.             type="string" column="LOGIN_NAME"/>
  15.         <property name="loginPassword"
  16.             type="string" column="LOGIN_PASSWD"/>
  17.         <property name="status"
  18.             type="string" column="STATUS"/>
  19.         <property name="createDate"
  20.             type="date" column="CREATE_DATE"/>
  21.         <property name="pauseDate"
  22.             type="date" column="PAUSE_DATE"/>
  23.         <property name="closeDate"
  24.             type="date" column="CLOSE_DATE"/>
  25.         <property name="realName"
  26.             type="string" column="REAL_NAME"/>
  27.         <property name="idcardNo"
  28.             type="string" column="IDCARD_NO"/>
  29.         <property name="birthdate"
  30.             type="date" column="BIRTHDATE"/>
  31.         <property name="gender"
  32.             type="string" column="GENDER"/>
  33.         <property name="occupation"
  34.             type="string" column="OCCUPATION"/>
  35.         <property name="telephone"
  36.             type="string" column="TELEPHONE"/>
  37.         <property name="email"
  38.             type="string" column="EMAIL"/>
  39.         <property name="mailaddress"
  40.             type="string" column="MAILADDRESS"/>
  41.         <property name="zipcode"
  42.             type="string" column="ZIPCODE"/>
  43.         <property name="qq"
  44.             type="string" column="QQ"/>
  45.         <property name="lastLoginTime"
  46.             type="date" column="LAST_LOGIN_TIME"/>
  47.         <property name="lastLoginIp"
  48.             type="string" column="LAST_LOGIN_IP"/>
  49.             
  50.         <!-- 配置services属性,采用一对多的关系 -->
  51.         <set name="services"
  52.             lazy="false" fetch="join"
  53.             cascade="all" inverse="true">
  54.             <!-- 用于指定关联条件,写关联条件的外键字段 -->
  55.             <key column="ACCOUNT_ID"/>
  56.             <!-- 用于指定采用哪种关系,加载哪方数据 -->
  57.             <one-to-many
  58.                 class="com.tarena.entity.Service"/>
  59.         </set>
  60.     </class>
  61. </hibernate-mapping>

步骤五:测试

再次执行test3()方法,控制台输出结果如下图,可以看出本次删除成功了,不但删除了账务账号数据,在此之前还删除了它所对应的所有业务账号。

图-13

4.4 完整代码

以下为本案例的完整代码。

其中Account.hbm.xml完整代码如下:

代码

TestCascade完整代码如下:

代码

5 HQL查询,按条件查询

5.1 问题

使用带条件的HQL 查询业务账号数据。

5.2 方案

在HQL中拼入条件,如name=?,然后在查询之前使用query给参数赋值。

5.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写按条件查询方法

在com.tarena.test包下创建测试类TestHQL,并在类中增加按条件查询的测试方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10.     /**
  11.      * 按条件查询
  12.      */
  13.     @Test
  14.     public void test1() {
  15.         String hql = "from Service where unixHost=?";
  16.         Session session = HibernateUtil.getSession();
  17.         Query query = session.createQuery(hql);
  18.         query.setString(0, "192.168.0.20");
  19.         List<Service> services = query.list();
  20.         for(Service service : services){
  21.             System.out.println(service.getId()
  22.                     + " " + service.getUnixHost()
  23.                     + " " + service.getOsUserName());
  24.         }
  25.         session.close();
  26.     }
  27.     
  28. }

步骤二:测试

执行test1()方法,控制台输出效果如下图,可以看到按条件查询出的结果:

图-14

5.4 完整代码

以下为本案例的完整代码。

其中TestHQL完整代码如下:

代码

6 HQL查询,查询一部分字段

6.1 问题

使用HQL查询,要求只查询一部分字段。

6.2 方案

可以通过select子句明确指定要返回的字段,注意HQL中select子句中写的是属性,而不是表中的字段。

6.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写查询一部分字段方法

在TestHQL中增加查询一部分字段的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10. // 其他查询方法略
  11.     /**
  12.      * 查询一部分字段
  13.      */
  14.     @Test
  15.     public void test2() {
  16.         String hql = "select id,unixHost,osUserName " +
  17.                 "from Service where unixHost=?";
  18.         Session session = HibernateUtil.getSession();
  19.         Query query = session.createQuery(hql);
  20.         query.setString(0, "192.168.0.20");
  21.         List<Object[]> services = query.list();
  22.         for(Object[] service : services) {
  23.             System.out.println(service[0]
  24.                     + " " + service[1]
  25.                     + " " + service[2]);
  26.         }
  27.         session.close();
  28.     }
  29.     
  30. }

步骤二:测试

执行test2(),控制台输出结果如下图,可以看到查询出来的这些字段的值。

图-15

6.4 完整代码

以下为本案例的完整代码。

其中TestHQL完整代码如下:

代码

7 HQL查询,分页查询

7.1 问题

按照每页显示3条数据,查询第1页的条件,查询出业务账号表中满足条件的记录,并且查询出总页数。

7.2 方案

  1. 分页查询时,在执行查询之前可以通过query对象设置分页参数值,query.setFirstResult(起点),以及query.setMaxResults(页容量)。
  2. 查询总页数,只需要通过hql查询出总行数,再根据总行数计算总页数即可,而查询总行数的hql格式为select count(*) from 对象。

7.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写分页查询的方法

在TestHQL中,增加分页查询的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10.     // 其他查询方法略
  11.     
  12.     /**
  13.      * 分页查询
  14.      */
  15.     @Test
  16.     public void test3() {
  17.         int page = 1;
  18.         int pageSize = 3;
  19.         String hql = "from Service order by id";
  20.         Session session = HibernateUtil.getSession();
  21.         Query query = session.createQuery(hql);
  22.         // 追加分页参数设置
  23.         int from = (page - 1) * pageSize;
  24.         query.setFirstResult(from);// 设置起点,从0开始
  25.         query.setMaxResults(pageSize);// 设置页容量
  26.         List<Service> services = query.list();
  27.         for (Service service : services) {
  28.             System.out.println(
  29.                 service.getId() + " "
  30.                 + service.getUnixHost() + " "
  31.                 + service.getOsUserName());
  32.         }
  33.         session.close();
  34.     }
  35.     
  36. }

步骤二:测试

执行test3(),控制台输出结果如下图,输出了第1页的3条数据。

图-16

步骤三:编写查询总页数的方法

在TestHQL中,增加查询总页数的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10. // 其他查询方法略
  11.     /**
  12.      * 查询总页数
  13.      */
  14.     @Test
  15.     public void test4() {
  16.         int pageSize=3;
  17.         String hql = "select count(*) from Service";
  18.         Session session = HibernateUtil.getSession();
  19.         Query query = session.createQuery(hql);
  20.         int rows = Integer.parseInt(query.uniqueResult().toString());
  21.         int totalPages = 0;
  22.         if(rows%pageSize == 0) {
  23.             totalPages = rows/pageSize;
  24.         } else {
  25.             totalPages = rows/pageSize+1;
  26.         }
  27.         System.out.println(totalPages);
  28.         session.close();
  29.     }
  30.     
  31. }

步骤四:测试

执行test4(),控制台输出结果如下图,输出了总页数。

图-17

7.4 完整代码

以下为本案例的完整代码。

其中TestHQL完整代码如下:

代码

8 HQL查询,多表联合查询

8.1 问题

使用HQL,联合查询出业务账号与账务账号的数据。

8.2 方案

HQL中多表联合查询的方式有3种,分别是

  1. 对象方式关联
  2. join方式关联
  3. select子句关联

使用这三种方式来做业务账号与账务账号的联合查询。

8.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写对象方式关联的方法

在TestHQL中,增加对象方式关联的查询方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10.     // 其他查询方法略
  11.     
  12.     /**
  13.      * 多表联合查询-对象方式关联
  14.      */
  15.     @Test
  16.     public void test5() {
  17.         String hql = "select " +
  18.                 "s.id," +
  19.                 "s.osUserName," +
  20.                 "s.unixHost, " +
  21.                 "a.id,a.realName," +
  22.                 "a.idcardNo " +
  23.                 "from Service s,Account a " +
  24.                 "where s.account.id=a.id ";
  25.         Session session = HibernateUtil.getSession();
  26.         Query query = session.createQuery(hql);
  27.         List<Object[]> list = query.list();
  28.         for (Object[] objs : list) {
  29.             System.out.println(
  30.                     objs[0] + " " +
  31.                     objs[1] + " " +
  32.                     objs[2] + " " +
  33.                     objs[3] + " " +
  34.                     objs[4] + " " +
  35.                     objs[5]);
  36.         }
  37.         session.close();
  38.     }
  39.     
  40. }

步骤二:测试

执行test5(),控制台输出结果如下图,可以查询出关联的数据。

图-18

步骤三:编写join方式关联的方法

在TestHQL中,增加join方式关联的查询方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10.     // 其他查询方法略
  11.     
  12.     /**
  13.      * 多表联合查询-join方式关联
  14.      */
  15.     @Test
  16.     public void test6() {
  17.         String hql = "select " +
  18.                 "s.id,s.osUserName," +
  19.                 "s.unixHost, " +
  20.                 "a.id," +
  21.                 "a.realName," +
  22.                 "a.idcardNo " +
  23.                 "from Service s join s.account a ";
  24.         Session session = HibernateUtil.getSession();
  25.         Query query = session.createQuery(hql);
  26.         List<Object[]> list = query.list();
  27.         for (Object[] objs : list) {
  28.             System.out.println(
  29.                     objs[0] + " " +
  30.                     objs[1] + " " +
  31.                     objs[2] + " " +
  32.                     objs[3] + " " +
  33.                     objs[4] + " " +
  34.                     objs[5]);
  35.         }
  36.         session.close();
  37.     }
  38.     
  39. }

步骤四:测试

执行test6(),控制台输出结果如下图,可以查询出关联的数据。

图-19

步骤五:编写select子句关联的方法

在TestHQL中,增加select子句关联的查询方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestHQL {
  9.     
  10. // 其他查询方法略    
  11.     /**
  12.      * 多表联合查询-select子句关联
  13.      */
  14.     @Test
  15.     public void test7() {
  16.         String hql = "select " +
  17.                 "id," +
  18.                 "osUserName," +
  19.                 "unixHost, " +
  20.                 "account.id," +
  21.                 "account.realName," +
  22.                 "account.idcardNo " +
  23.                 "from Service ";
  24.         Session session = HibernateUtil.getSession();
  25.         Query query = session.createQuery(hql);
  26.         List<Object[]> list = query.list();
  27.         for (Object[] objs : list) {
  28.             System.out.println(
  29.                     objs[0] + " " +
  30.                     objs[1] + " " +
  31.                     objs[2] + " " +
  32.                     objs[3] + " " +
  33.                     objs[4] + " " +
  34.                     objs[5]);
  35.         }
  36.         session.close();
  37.     }
  38.     
  39. }

步骤六:测试

执行test7(),控制台输出结果如下,可以查询出关联的数据。

图-20

8.4 完整代码

以下为本案例的完整代码。

其中TestHQL完整代码如下:

代码

9 Hibernate中的SQL查询

9.1 问题

在Hibernate中直接使用SQL查询业务账号数据。

9.2 方案

Hibernate中可以通过SQLQuery对象来执行原始的SQL。

9.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写使用SQL查询的方法

在com.tarena.test包下,创建测试类TestOtherQuery,并在类中增加使用SQL查询的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.SQLQuery;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestOtherQuery {
  9.     
  10.     /**
  11.      * 使用SQL查询
  12.      */
  13.     @Test
  14.     public void test1() {
  15.         String sql = "select * " +
  16.                 "from SERVICE where unix_host=?";
  17.         Session session = HibernateUtil.getSession();
  18.         SQLQuery query = session.createSQLQuery(sql);
  19.         query.setString(0, "192.168.0.20");
  20.         query.addEntity(Service.class);
  21.         //采用Service类型封装一条数据
  22.         List<Service> list = query.list();
  23.         for(Service service : list){
  24.             System.out.println(
  25.                 service.getId() + " " +
  26.                 service.getOsUserName() + " " +
  27.                 service.getUnixHost() + " "
  28.             );
  29.         }
  30.         session.close();
  31.     }
  32.     
  33. }

步骤二:测试

执行test1(),控制台输出结果如下图,是直接执行SQL所查询到的内容:

图-21

9.4 完整代码

以下为本案例的完整代码。

其中TestOtherQuery完整代码如下:

代码

10 使用二级缓存

10.1 问题

在查询员工时,使用二级缓存,使得不同的session可以共享这些员工数据。

10.2 方案

使用ehcache缓存组件来支持二级缓存,二级缓存的使用步骤为

  1. 导入二级缓存驱动包
  2. 引入二级缓存配置文件
  3. 在hibernate.cfg.xml中开启二级缓存并指定二级缓存驱动类
  4. 在映射关系文件中设置缓存策略
  5. 执行查询

10.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:导入二级缓存驱动包

导入二级缓存驱动包ehcache-1.2.3.jar,导入后项目中包结构如下图:

图-22

步骤二:引入二级缓存配置文件

引入二级缓存配置文件ehcache.xml,并配置缓存参数,代码如下:

  1. <ehcache>
  2. <!--
  3.     缓存到硬盘时的缓存路径,java.io.tmpdir表示
  4.     系统默认缓存路径。
  5. -->
  6. <diskStore path="java.io.tmpdir"/>
  7. <!--
  8.     默认缓存配置。
  9.     maxElementsInMemory:
  10.         二级缓存可容纳最大对象数。
  11.     eternal:
  12.         是否保持二级缓存中对象不变。
  13.     timeToIdleSeconds:
  14.         允许对象空闲的时间,即对象最后一次访问起,超过该时间即失效。
  15.     timeToLiveSeconds:
  16.         允许对象存活的时间,即对象创建起,超过该时间即失效。
  17.     overflowToDisk:
  18.         内存不足时,是否允许使用硬盘缓存,写入路径参见diskStore。
  19. -->
  20. <defaultCache
  21. maxElementsInMemory="300"
  22. eternal="false"
  23. timeToIdleSeconds="120"
  24. timeToLiveSeconds="300"
  25. overflowToDisk="true"
  26. />
  27.     <!-- 自定义缓存配置 -->
  28. <cache name="myCache"
  29. maxElementsInMemory="2000"
  30. eternal="false"
  31. timeToIdleSeconds="300"
  32. timeToLiveSeconds="600"
  33. overflowToDisk="true"
  34. />
  35. </ehcache>

步骤三:开启二级缓存、指定二级缓存驱动类

在hibernate.cfg.xml中开启二级缓存,并指定二级缓存驱动类,代码如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE hibernate-configuration PUBLIC
  3.         "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  4.         "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
  5. <hibernate-configuration>
  6.     <session-factory>
  7.         <!-- 数据库连接信息,根据自己的数据库进行配置 -->
  8.         <property name="connection.url">
  9.             jdbc:oracle:thin:@localhost:1521:xe
  10.         </property>
  11.         <property name="connection.username">lhh</property>
  12.         <property name="connection.password">123456</property>
  13.         <property name="connection.driver_class">
  14.             oracle.jdbc.OracleDriver
  15.         </property>
  16.         
  17.         <!-- Hibernate配置信息 -->
  18.         <!-- dialect方言,用于配置生成针对哪个数据库的SQL语句 -->
  19.         <property name="dialect">
  20.             <!-- 方言类,Hibernate提供的,用于封装某种特定数据库的方言 -->
  21.             org.hibernate.dialect.OracleDialect
  22.         </property>
  23.         <!-- Hibernate生成的SQL是否输出到控制台 -->
  24.         <property name="show_sql">true</property>
  25.         <!-- 将SQL输出时是否格式化。为了方便截图,我将其设置为false -->
  26.         <property name="format_sql">false</property>
  27.         
  28.         <!-- 开启二级缓存 -->
  29.         <property name="hibernate.cache.use_second_level_cache">
  30.             true
  31.         </property>
  32.         <!-- 指定所采用的二级缓存驱动类 -->
  33.         <property name="hibernate.cache.provider_class">
  34.             org.hibernate.cache.EhCacheProvider
  35.         </property>        
  36.         
  37.         <!-- 声明映射关系文件 -->
  38.         <mapping resource="com/tarena/entity/Emp.hbm.xml" />
  39.         <mapping resource="com/tarena/entity/Account.hbm.xml" />
  40.         <mapping resource="com/tarena/entity/Service.hbm.xml" />        
  41.     </session-factory>
  42. </hibernate-configuration>

步骤四:设置缓存策略

在Emp.hbm.xml中,指定二级缓存策略为只读的,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4.     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  5. <hibernate-mapping>
  6.     <!-- 配置实体类和表的关系 -->
  7.     <class name="com.tarena.entity.Emp" table="emp">
  8.         <!--
  9.             开启二级缓存,指定二级缓存策略。
  10.             可以用region属性指定自定义的缓存设置。
  11.         -->
  12.         <cache usage="read-only" />
  13.         
  14.         <!-- 配置主键属性和字段的关系 -->
  15.         <id name="id" type="integer" column="id">
  16.             <!-- 用来指明主键的生成方式 -->
  17.             <generator class="sequence">
  18.                 <!-- 指定用于生成主键的sequence -->
  19.                 <param name="sequence">emp_seq</param>
  20.             </generator>
  21.         </id>
  22.         
  23.         <!-- 配置实体类中属性与表中字段的关系 -->
  24.         <property name="name"
  25.             type="string" column="name"/>
  26.         <property name="age"
  27.             type="integer" column="age"/>
  28.         <property name="salary"
  29.             type="double" column="salary"/>
  30.         <property name="birthday"
  31.             type="date" column="birthday"/>
  32.         <property name="lastLoginTime"
  33.             type="timestamp" column="last_login_time"/>
  34.         <property name="marry"
  35.             type="yes_no" column="marry"/>
  36.     </class>
  37. </hibernate-mapping>

步骤五:编写查询方法,测试二级缓存

在com.tarena.test包下,创建一个测试类TestSecondCache,增加一个测试二级缓存的方法,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Emp;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestSecondCache {
  9.     /**
  10.      * 二级缓存
  11.      */
  12.     @Test
  13.     public void test1() {
  14.         Session session1 = HibernateUtil.getSession();
  15.         Emp e1 = (Emp) session1.get(Emp.class, 321);
  16.         System.out.println(e1.getName());
  17.         
  18.         System.out.println("-----------------");
  19.         
  20.         Session session2 = HibernateUtil.getSession();
  21.         Emp e2 = (Emp) session2.get(Emp.class, 321);
  22.         System.out.println(e2.getName());
  23.         
  24.         session1.close();
  25.         session2.close();
  26.     }
  27.     
  28. }

步骤六:测试

执行test1(),控制台输出结果如下图,可见第二次查询没有访问数据库,是二级缓存存在的缘故:

图-23

步骤七:管理二级缓存

二级缓存是SessionFactory级缓存,由它负责管理,因此需要获取到SessionFactory才能管理二级缓存,我们先在HibernateUtil中增加获取SessionFactory的方法,代码如下:

  1. package com.tarena.util;
  2. import org.hibernate.Session;
  3. import org.hibernate.SessionFactory;
  4. import org.hibernate.cfg.Configuration;
  5. public class HibernateUtil {
  6.     private static SessionFactory sessionFactory;
  7.     
  8.     static {
  9.         // 加载Hibernate主配置文件
  10.         Configuration conf = new Configuration();
  11.         conf.configure("/hibernate.cfg.xml");
  12.         sessionFactory = conf.buildSessionFactory();
  13.     }
  14.     /**
  15.      * 创建session
  16.      */
  17.     public static Session getSession() {
  18.         return sessionFactory.openSession();
  19.     }
  20.     
  21.     /**
  22.      * 返回SessionFactory
  23.      */
  24.     public static SessionFactory getSessionFactory() {
  25.         return sessionFactory;
  26.     }
  27.     
  28.     public static void main(String[] args) {
  29.         System.out.println(getSession());
  30.     }
  31. }

修改test1()方法,在第二次查询前清理二级缓存,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Emp;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestSecondCache {
  9.     /**
  10.      * 二级缓存
  11.      */
  12.     @Test
  13.     public void test1() {
  14.         Session session1 = HibernateUtil.getSession();
  15.         Emp e1 = (Emp) session1.get(Emp.class, 321);
  16.         System.out.println(e1.getName());
  17.         
  18.         System.out.println("-----------------");
  19.         HibernateUtil.getSessionFactory().evict(Emp.class);
  20.         
  21.         Session session2 = HibernateUtil.getSession();
  22.         Emp e2 = (Emp) session2.get(Emp.class, 321);
  23.         System.out.println(e2.getName());
  24.         
  25.         session1.close();
  26.         session2.close();
  27.     }
  28.     
  29. }

再次执行test1(),控制台输出结果如下图,由于清理了二级缓存,因此第二次查询时访问了数据库。

图-24

10.4 完整代码

以下是本案例的完整代码。

其中缓存配置文件ehcache.xml完整代码如下:

代码

hibernate.cfg.xml完整代码如下:

代码

emp.hbm.xml完整代码如下:

代码

TestSecondCache完整代码如下:

代码

11 使用查询缓存

11.1 问题

在查询多条员工数据时,使用查询缓存,缓存查询的HQL,使得再次使用同样HQL查询时不必重新访问数据库。

11.2 方案

查询缓存的使用步骤是

  1. 开启二级缓存
  2. 在hibernate.cfg.xml中开启查询缓存
  3. 在查询之前,开启查询缓存

11.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:开启二级缓存

开启二级缓存,由于上个案例已经完成,这一步就可以省略。但是要注意,查询缓存是基于二级缓存的,使用查询缓存的前提是开启二级缓存。

步骤二:开启查询缓存

在hibernate.cfg.xml中开启查询缓存,代码如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE hibernate-configuration PUBLIC
  3.         "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  4.         "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
  5. <hibernate-configuration>
  6.     <session-factory>
  7.         <!-- 数据库连接信息,根据自己的数据库进行配置 -->
  8.         <property name="connection.url">
  9.             jdbc:oracle:thin:@localhost:1521:xe
  10.         </property>
  11.         <property name="connection.username">lhh</property>
  12.         <property name="connection.password">123456</property>
  13.         <property name="connection.driver_class">
  14.             oracle.jdbc.OracleDriver
  15.         </property>
  16.         
  17.         <!-- Hibernate配置信息 -->
  18.         <!-- dialect方言,用于配置生成针对哪个数据库的SQL语句 -->
  19.         <property name="dialect">
  20.             <!-- 方言类,Hibernate提供的,用于封装某种特定数据库的方言 -->
  21.             org.hibernate.dialect.OracleDialect
  22.         </property>
  23.         <!-- Hibernate生成的SQL是否输出到控制台 -->
  24.         <property name="show_sql">true</property>
  25.         <!-- 将SQL输出时是否格式化。为了方便截图,我将其设置为false -->
  26.         <property name="format_sql">false</property>
  27.         
  28.         <!-- 开启二级缓存 -->
  29.         <property name="hibernate.cache.use_second_level_cache">
  30.             true
  31.         </property>
  32.         <!-- 指定所采用的二级缓存驱动 -->
  33.         <property name="hibernate.cache.provider_class">
  34.             org.hibernate.cache.EhCacheProvider
  35.         </property>        
  36.         <!-- 开启查询缓存 -->
  37.         <property name="hibernate.cache.use_query_cache">
  38.             true
  39.         </property>
  40.                 
  41.         <!-- 声明映射关系文件 -->
  42.         <mapping resource="com/tarena/entity/Emp.hbm.xml" />
  43.         <mapping resource="com/tarena/entity/Account.hbm.xml" />
  44.         <mapping resource="com/tarena/entity/Service.hbm.xml" />        
  45.     </session-factory>
  46. </hibernate-configuration>

步骤三:查询前开启查询缓存

在TestSecondCache中,增加测试查询缓存的方法,使用相同的HQL执行2次查询,每次查询前都设置开启查询缓存,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Emp;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestSecondCache {
  9.     /**
  10.      * 二级缓存
  11.      */
  12.     @Test
  13.     public void test1() {
  14.         Session session1 = HibernateUtil.getSession();
  15.         Emp e1 = (Emp) session1.get(Emp.class, 321);
  16.         System.out.println(e1.getName());
  17.         
  18.         System.out.println("-----------------");
  19.         HibernateUtil.getSessionFactory().evict(Emp.class);
  20.         
  21.         Session session2 = HibernateUtil.getSession();
  22.         Emp e2 = (Emp) session2.get(Emp.class, 321);
  23.         System.out.println(e2.getName());
  24.         
  25.         session1.close();
  26.         session2.close();
  27.     }
  28.     
  29.     /**
  30.      * 查询缓存
  31.      */
  32.     @Test
  33.     public void test2() {
  34.         Session session = HibernateUtil.getSession();
  35.         
  36.         String hql = "from Emp";
  37.         Query query = session.createQuery(hql);
  38.         // 开启查询缓存
  39.         query.setCacheable(true);
  40.         List<Emp> emps = query.list();
  41.         for(Emp e : emps) {
  42.             System.out.println(e.getId() + " " + e.getName());
  43.         }
  44.         
  45.         System.out.println("---------------");
  46.         
  47.         hql = "from Emp";
  48.         query = session.createQuery(hql);
  49.         // 开启查询缓存
  50.         query.setCacheable(true);
  51.         emps = query.list();
  52.         for(Emp e : emps) {
  53.             System.out.println(e.getId() + " " + e.getName());
  54.         }
  55.         
  56.         session.close();
  57.     }
  58.     
  59. }

步骤四:测试

执行test2(),控制台输出结果如下图,可见在使用相同HQL查询时,第二次查询不必再次访问数据库。

图-25

步骤五:管理查询缓存

修改test2(),在第二次查询之前清理查询缓存,代码如下:

  1. package com.tarena.test;
  2. import java.util.List;
  3. import org.hibernate.Query;
  4. import org.hibernate.Session;
  5. import org.junit.Test;
  6. import com.tarena.entity.Emp;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestSecondCache {
  9.     /**
  10.      * 二级缓存
  11.      */
  12.     @Test
  13.     public void test1() {
  14.         Session session1 = HibernateUtil.getSession();
  15.         Emp e1 = (Emp) session1.get(Emp.class, 321);
  16.         System.out.println(e1.getName());
  17.         
  18.         System.out.println("-----------------");
  19.         HibernateUtil.getSessionFactory().evict(Emp.class);
  20.         
  21.         Session session2 = HibernateUtil.getSession();
  22.         Emp e2 = (Emp) session2.get(Emp.class, 321);
  23.         System.out.println(e2.getName());
  24.         
  25.         session1.close();
  26.         session2.close();
  27.     }
  28.     
  29.     /**
  30.      * 查询缓存
  31.      */
  32.     @Test
  33.     public void test2() {
  34.         Session session = HibernateUtil.getSession();
  35.         
  36.         String hql = "from Emp";
  37.         Query query = session.createQuery(hql);
  38.         // 开启查询缓存
  39.         query.setCacheable(true);
  40.         List<Emp> emps = query.list();
  41.         for(Emp e : emps) {
  42.             System.out.println(e.getId() + " " + e.getName());
  43.         }
  44.         
  45.         System.out.println("---------------");
  46.         HibernateUtil.getSessionFactory().evictQueries();
  47.         
  48.         hql = "from Emp";
  49.         query = session.createQuery(hql);
  50.         // 开启查询缓存
  51.         query.setCacheable(true);
  52.         emps = query.list();
  53.         for(Emp e : emps) {
  54.             System.out.println(e.getId() + " " + e.getName());
  55.         }
  56.         
  57.         session.close();
  58.     }
  59.     
  60. }

再次执行test2(),控制台输出结果如下图,可见第二次查询也访问了数据库,主要是清理了查询缓存中数据的缘故。

图-26

11.4 完整代码

以下为本案例的完整代码。

其中Hibernate.cfg.xml完整代码如下:

代码

TestSecondCache完整代码如下:

代码
0 0
原创粉丝点击