hiberate学习二 缓存

Top

JAVA Hibernate DAY02

  1. 验证一级缓存
  2. 管理一级缓存
  3. 验证持久态对象的特性
  4. 验证延迟加载
  5. 在NETCTOSS中使用延迟加载
  6. 使用一对多关联映射

1 验证一级缓存

1.1 问题

设计几个测试案例,以验证一级缓存的存在及特性。

1.2 方案

  1. 用同一个Session查询同一条数据2次,如果只查询一次数据库,则验证了一级缓存的存在。
  2. 用2个不同的Session,分别查询同一条数据,如果查询2次数据库,则验证了一级缓存是Session独享的。

1.3 步骤

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

步骤一:创建项目

复制项目HibernateDay01,粘贴并将项目名改为HibernateDay02。

步骤二:写测试案例代码

在com.tarena.test包下创建测试类TestFirstCache,在这个测试类中分别写出方案中提到的2个测试方法,用以验证一级缓存的存在及特性,代码如下:


    
    
  1. package com.tarena.test;
  2. import org.hibernate.Session;
  3. import org.junit.Test;
  4. import com.tarena.entity.Emp;
  5. import com.tarena.util.HibernateUtil;
  6. public class TestFirstCache {
  7.     
  8.     /**
  9.      * 用同一个Session查询同一条数据2次,
  10.      * 如果只查询一次数据库,则验证了一级缓存的存在。
  11.      */
  12.     @Test
  13.     public void test1() {
  14.         Session session = HibernateUtil.getSession();
  15.         Emp e1 = (Emp) session.get(Emp.class, 321);
  16.         System.out.println(e1.getName());
  17.         System.out.println("----------------");
  18.         Emp e2 = (Emp) session.get(Emp.class, 321);
  19.         System.out.println(e2.getName());
  20.         session.close();
  21.     }
  22.     /**
  23.      * 用2个不同的Session,分别查询同一条数据,
  24.      * 如果查询2次数据库,则验证了一级缓存是Session独享的。
  25.      */
  26.     @Test
  27.     public void test2() {
  28.         Session session1 = HibernateUtil.getSession();
  29.         Emp e1 = (Emp) session1.get(Emp.class, 321);
  30.         System.out.println(e1.getName());
  31.         Session session2 = HibernateUtil.getSession();
  32.         Emp e2 = (Emp) session2.get(Emp.class, 321);
  33.         System.out.println(e2.getName());
  34.         session1.close();
  35.         session2.close();
  36.     }
  37.     
  38. }

步骤三:测试

分别执行以上2个方法,根据控制台输出的结果,根据其SQL语句数量可以判断出查询执行的次数,进而验证一级缓存的存在及特性。

test1()执行后,控制台输出结果如下图,可以看出第二次查询并没有真正访问数据库,验证了一级缓存的存在:

图-1

test2()执行后,控制台输出结果如下图,可以看出第二次查询访问了数据库,验证了一级缓存是Session独享的。

图-2

1.4 完整代码

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

其中TestFirstCache完整代码如下:

2 管理一级缓存

2.1 问题

掌握一级缓存管理的2种方式:

  1. 使用evict方法,从一级缓存中移除一个对象。
  2. 使用clear方法,将一级缓存中的对象全部移除。

设计出案例,来使用并验证一级缓存管理方法。

2.2 方案

设计2个案例,使用同一个Session查询同一条数据2次,由于一级缓存的存在,第二次查询时将从一级缓存中取数,而不会查询数据库。

那么,如果在第二次查询之前将数据从缓存中移除,第二次查询时就会访问数据库。在这两个案例中,我们分别使用evict和clear方法将数据从缓存中移除。

2.3 步骤

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

步骤一:在TestFirstCache中增加测试案例代码

在TestFirstCache中增加2个方法,均使用同一个Session查询同一条数据2次,在第二次查询之前,分别使用evice和clear方法移除缓存数据,代码如下:


    
    
  1. package com.tarena.test;
  2. import org.hibernate.Session;
  3. import org.junit.Test;
  4. import com.tarena.entity.Emp;
  5. import com.tarena.util.HibernateUtil;
  6. public class TestFirstCache {
  7.     
  8.     /**
  9.      * 用同一个Session查询同一条数据2次,
  10.      * 如果只查询一次数据库,则验证了一级缓存的存在。
  11.      */
  12.     @Test
  13.     public void test1() {
  14.         Session session = HibernateUtil.getSession();
  15.         Emp e1 = (Emp) session.get(Emp.class, 321);
  16.         System.out.println(e1.getName());
  17.         System.out.println("----------------");
  18.         Emp e2 = (Emp) session.get(Emp.class, 321);
  19.         System.out.println(e2.getName());
  20.         session.close();
  21.     }
  22.     /**
  23.      * 用2个不同的Session,分别查询同一条数据,
  24.      * 如果查询2次数据库,则验证了一级缓存是Session独享的。
  25.      */
  26.     @Test
  27.     public void test2() {
  28.         Session session1 = HibernateUtil.getSession();
  29.         Emp e1 = (Emp) session1.get(Emp.class, 321);
  30.         System.out.println(e1.getName());
  31.         Session session2 = HibernateUtil.getSession();
  32.         Emp e2 = (Emp) session2.get(Emp.class, 321);
  33.         System.out.println(e2.getName());
  34.         session1.close();
  35.         session2.close();
  36.     }
  37.     
  38.     /**
  39.      * 验证缓存管理的方法evict
  40.      */
  41.     @Test
  42.     public void test3() {
  43.         Session session = HibernateUtil.getSession();
  44.         Emp e1 = (Emp) session.get(Emp.class, 321);
  45.         System.out.println(e1.getName());
  46.         session.evict(e1);
  47.         Emp e2 = (Emp) session.get(Emp.class, 321);
  48.         System.out.println(e2.getName());
  49.         session.close();
  50.     }
  51.     
  52.     /**
  53.      * 验证缓存管理的方法clear
  54.      */
  55.     @Test
  56.     public void test4() {
  57.         Session session = HibernateUtil.getSession();
  58.         Emp e1 = (Emp) session.get(Emp.class, 321);
  59.         System.out.println(e1.getName());
  60.         session.clear();
  61.         Emp e2 = (Emp) session.get(Emp.class, 321);
  62.         System.out.println(e2.getName());
  63.         session.close();
  64.     }
  65.     
  66. }

步骤二:测试

分别执行这两个方法,并观察控制台,会发现都只输出了一次SQL,验证了这2个方法可以有效的管理一级缓存。

test3()执行后,控制台输出结果如下图,可以看出第二次查询访问了数据库,验证了evice管理一级缓存是有效的。

图-3

test4()执行后,控制台输出结果如下图,可以看出第二次查询访问了数据库,验证了clear管理一级缓存是有效的。

图-4

2.4 完整代码

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

其中TestFirstCache完整代码如下:

3 验证持久态对象的特性

3.1 问题

设计出案例,验证持久态对象的特性:

  1. 持久态对象存在于一级缓存中。
  2. 持久态对象可以自动更新至数据库。
  3. 持久态对象自动更新数据库的时机是session.flush()。

3.2 方案

设计3个案例,分别验证持久态对象的3个特性:

  1. 新增一条数据,在新增后该数据对象为持久态的,然后根据对象的ID再次查询数据。执行时如果控制台不重新输出SQL则验证了持久态对象存在于一级缓存。
  2. 新增一条数据,在新增后该对象为持久态的,然后修改这个对象的任意属性值,并提交事务。在执行时,如果发现数据库中的数据是修改后的内容,则验证了持久态对象可以自动更新至数据库。
  3. 查询一条数据,该数据对象为持久态的,然后修改对象的任意属性值,再调用session.flush()方法,并且不提交事务。如果执行时控制台输出更新的SQL,则验证了一级缓存对象更新至数据库的时机为session.flush()。

3.3 步骤

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

步骤一:创建验证对象持久性测试类

在com.tarena.test包下,创建一个测试类TestPersistent,在其中写出验证对象持久性的3个测试方法,用以验证对象持久性的3个特性,代码如下:


    
    
  1. package com.tarena.test;
  2. import java.sql.Date;
  3. import java.sql.Timestamp;
  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.Emp;
  9. import com.tarena.util.HibernateUtil;
  10. public class TestPersistent {
  11.     
  12.     /**
  13.      * 持久态对象存在于一级缓存中
  14.      */
  15.     @Test
  16.     public void test1() {
  17.         Emp e = new Emp();
  18.         e.setName("唐僧");
  19.         e.setAge(29);
  20.         e.setMarry(false);
  21.         e.setSalary(12000.00);
  22.         e.setBirthday(
  23.             Date.valueOf("1983-10-20"));
  24.         e.setLastLoginTime(
  25.             new Timestamp(System.currentTimeMillis()));
  26.         Session session = HibernateUtil.getSession();
  27.         Transaction ts = session.beginTransaction();
  28.         try {
  29.             session.save(e);
  30.             ts.commit();
  31.         } catch (HibernateException e1) {
  32.             e1.printStackTrace();
  33.             ts.rollback();
  34.         }
  35.         
  36.         Emp emp = (Emp) session.get(Emp.class, e.getId());
  37.         System.out.println(emp.getId() + " " + emp.getName());
  38.         
  39.         session.close();
  40.     }
  41.     /**
  42.      * 持久态对象可以自动更新至数据库
  43.      */
  44.     @Test
  45.     public void test2() {
  46.         Emp e = new Emp();
  47.         e.setName("孙悟空");
  48.         e.setAge(29);
  49.         e.setMarry(false);
  50.         e.setSalary(12000.00);
  51.         e.setBirthday(
  52.             Date.valueOf("1983-10-20"));
  53.         e.setLastLoginTime(
  54.             new Timestamp(System.currentTimeMillis()));
  55.         Session session = HibernateUtil.getSession();
  56.         Transaction ts = session.beginTransaction();
  57.         try {
  58.             session.save(e);
  59.             e.setName("猪八戒");
  60.             ts.commit();
  61.         } catch (HibernateException e1) {
  62.             e1.printStackTrace();
  63.             ts.rollback();
  64.         }
  65.         session.close();
  66.     }    
  67.     /**
  68.      * 持久态对象自动更新数据库的时机
  69.      */
  70.     @Test
  71.     public void test3() {
  72.         Session session = HibernateUtil.getSession();
  73.         Emp e = (Emp) session.load(Emp.class, 201);
  74.         e.setName("太上老君");
  75.         session.flush(); //同步但未提交事务
  76.         session.close();
  77.     }
  78.     
  79. }

步骤二:测试

分别执行上述3个测试方法,观察控制台输出的SQL,看是否与方案中描述的一致。

test1()执行后,控制台输出结果如下图,可以看出后面的查询并没有再次访问数据库,验证了持久态对象是存在于一级缓存中的。

图-5

test2()执行后,控制台输出结果如下图,可以看出Hibernate自动执行了一次更新,验证了持久态对象会自动更新至数据库。

图-6

EMP表查询结果如下图,可以看出最后一条数据的名称的确被更新为猪八戒。

图-7

test3()执行后,控制台输出结果如下图,可以看出Hibernate自动执行了一次更新,验证了自动更新的时机是session.flush()。

图-8

EMP表查询结果如下图,可以看出最后一条数据的名称并没有更新为太上老君,说明session.flush只是触发更新,并没有提交事务。

图-9

3.4 完整代码

本案例的完整代码如下所示:

TestPersistent完整代码如下:

4 验证延迟加载

4.1 问题

设计案例,验证session.load()方法和query.iterate()方法在查询时是采用延迟加载机制的。

4.2 方案

  1. 验证session.load()的延迟加载,可以先查询某条EMP数据,然后输出一个分割线,在分割线的后面再使用这个对象,输出它的一些属性值。在运行时,如果查询EMP的SQL输出在分割线的后面,则验证了该方法是采用延迟加载机制的。
  2. 验证query.iterate()的延迟加载,可以先查询出全部的EMP数据,然后输出一个分割线,在分割线的后面再遍历查询结果并输出每个对象的一些属性值。在运行时,如果查询EMP的SQL输出在分割线的后面,则验证了该方法是采用延迟加载机制的。

4.3 步骤

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

步骤一:创建验证延迟加载的测试类

在com.tarena.test包下,创建一个测试类TestLazy,并在这个类中写2个测试方法,分别验证session.load()和query.iterate()方法是采用延迟加载机制的,代码如下:


    
    
  1. package com.tarena.test;
  2. import java.util.Iterator;
  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 TestLazy {
  9.     
  10.     /**
  11.      * 验证load方法是延迟加载的
  12.      */
  13.     @Test
  14.     public void test1() {
  15.         Session session = HibernateUtil.getSession();
  16.         // load方法并没有触发访问数据库
  17.         Emp emp = (Emp) session.load(Emp.class, 321);
  18.         System.out.println("-----------------");
  19.         // 使用emp对象时才真正访问数据库
  20.         System.out.println(emp.getName());
  21.         session.close();
  22.     }
  23.     /**
  24.      * 验证iterate方法是延迟加载的
  25.      */
  26.     @Test
  27.     public void test2() {
  28.         String hql = "from Emp";
  29.         Session session = HibernateUtil.getSession();
  30.         Query query = session.createQuery(hql);
  31.         // iterate方法访问了数据库,但只查询了ID列
  32.         Iterator<Emp> it = query.iterate();
  33.         System.out.println("-----------------");
  34.         while (it.hasNext()) {
  35.             Emp emp = it.next();
  36.             // 使用emp对象时才将其他列全部加载
  37.             System.out.println(emp.getName());
  38.         }
  39.         session.close();
  40.     }
  41.     
  42. }

步骤二:测试

分别执行上面的2个测试方法,根据控制台输出的结果,判断session.load()和query.iterate()方法是否采用延迟加载机制。

test1()方法执行后,控制台输出结果如下图,可以看出查询SQL是输出在分割线后面的,也就是在使用emp对象时才触发了数据库的访问,验证了延迟加载的存在。

图-10

test2()方法执行后,控制台输出结果如下图,可以看出查询EMP表全部内容的SQL输出在分割线的后面,也就是在使用emp对象时才触发的数据库访问,验证了延迟加载的存在。

图-11

4.4 完整代码

本案例的完整代码如下所示:

5 在NETCTOSS中使用延迟加载

5.1 问题

请将NETCTOSS中资费模块DAO的实现,改用Hibernate中延迟加载的方法。

5.2 方案

将CostDaoImpl中的findById方法的实现,改为session.load()。

为了避免session提前关闭导致延迟加载出现问题,需要在findById方法中不关闭session,而是自定义拦截器,在拦截器中调用完action之后关闭session。自然地,资费模块的action都应该引用这个拦截器。

5.3 步骤

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

步骤一:使用延迟加载方法

将CostDaoImpl中的findById方法中,session.get()改为session.load(),代码如下:


    
    
  1. package com.netctoss.dao;
  2. import java.util.List;
  3. import org.hibernate.HibernateException;
  4. import org.hibernate.Query;
  5. import org.hibernate.Session;
  6. import org.hibernate.Transaction;
  7. import com.netctoss.entity.Cost;
  8. import com.netctoss.util.HibernateUtil;
  9. /**
  10. *    当前阶段学习重点是Struts2,对于DAO的实现就模拟实现了。
  11. *    同学们可以使用JDBC/MyBatis自行实现该DAO。
  12. */
  13. public class CostDaoImpl implements ICostDao {
  14.     @Override
  15.     public List<Cost> findAll() {
  16.         String hql = "from Cost";
  17.         Session session = HibernateUtil.getSession();
  18.         Query query = session.createQuery(hql);
  19.         List<Cost> list = query.list();
  20.         session.close();
  21.         return list;
  22.     }
  23.     @Override
  24.     public void delete(int id) {
  25.         Cost cost = new Cost();
  26.         cost.setId(id);
  27.         Session session = HibernateUtil.getSession();
  28.         Transaction ts = session.beginTransaction();
  29.         try {
  30.             session.delete(cost);
  31.             ts.commit();
  32.         } catch (HibernateException e) {
  33.             e.printStackTrace();
  34.             ts.rollback();
  35.         } finally {
  36.             session.close();
  37.         }
  38.     }
  39.     @Override
  40.     public Cost findByName(String name) {
  41.         // 模拟根据名称查询资费数据,假设资费表中只有一条名为tarena的数据
  42.         if("tarena".equals(name)) {
  43.             Cost c = new Cost();
  44.             c.setId(97);
  45.             c.setName("tarena");
  46.             c.setBaseDuration(99);
  47.             c.setBaseCost(9.9);
  48.             c.setUnitCost(0.9);
  49.             c.setDescr("tarena套餐");
  50.             c.setStatus("0");
  51.             c.setCostType("2");
  52.             return c;
  53.         }
  54.         return null;
  55.     }
  56.     @Override
  57.     public Cost findById(int id) {
  58.         Session session = HibernateUtil.getSession();
  59.         Cost cost = (Cost) session.load(Cost.class, id);
  60.         session.close();
  61.         return cost;
  62.     }
  63. }

步骤二:测试

重新部署项目,并重启tomcat,访问资费修改页面,效果如下图:

图-12

可以看出要修改的数据,只有ID显示正确,其他字段都为空。原因是我们的findById方法中直接关闭了session,而该方法返回的对象是在JSP中使用的,在使用时session已经关闭,由于延迟加载机制的存在,导致了这个问题的发生。要想解决这个问题,我们需要继续如下的步骤。

步骤三:重构HibernateUtil,使用ThreadLocal管理Session

重构HibernateUtil,引入ThreadLocal来管理Session。ThreadLocal对象是与线程有关的工具类,它的目的是将管理的对象按照线程进行隔离,以保证一个线程只对应一个对象。

由于后面我们要在拦截器中关闭连接,因此需要准确的取出DAO中使用的连接对象,为了便于实现在一个线程(一次客户端请求,就是一个线程)中,不同的代码位置获取同一个连接,那么使用ThreadLocal来管理session就再合适不过了。

注意,引入ThreadLocal管理session,不仅仅是在创建session时将其加入到ThreadLocal中,在关闭session时也需要将其从ThreadLocal中移除,因此HibernateUtil中还需要提供一个关闭session的方法。

重构以后,HibernateUtil代码如下:


    
    
  1. package com.netctoss.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.      * 使用ThreadLocal管理Session,可以保证一个线程中只有唯一的一个连接。
  9.      * 并且我们在获取连接时,它会自动的给我们返回当前线程对应的连接。
  10.      */
  11.     private static ThreadLocal<Session> tl =
  12.             new ThreadLocal<Session>();
  13.     
  14.     static {
  15.         // 加载Hibernate主配置文件
  16.         Configuration conf = new Configuration();
  17.         conf.configure("/hibernate.cfg.xml");
  18.         sessionFactory = conf.buildSessionFactory();
  19.     }
  20.     /**
  21.      * 创建session
  22.      */
  23.     public static Session getSession() {
  24.         // ThreadLocal会以当前线程名为key获取连接
  25.         Session session = tl.get();
  26.         // 如果取到的当前线程的连接为空
  27.         if(session == null) {
  28.             // 使用工厂创建连接
  29.             session = sessionFactory.openSession();
  30.             // ThreadLocal会以当前线程名为key保存session
  31.             tl.set(session);
  32.         }
  33.         return session;
  34.     }
  35.     
  36.     /**
  37.      * 关闭session
  38.      */
  39.     public static void close() {
  40.         // ThreadLocal会以当前线程名为key获取连接
  41.         Session session = tl.get();
  42.         // 如果取到的当前线程的连接不为空
  43.         if(session != null) {
  44.             // 关闭session
  45.             session.close();
  46.             // 将当前线程对应的连接从ThreadLocal中移除
  47.             tl.remove();
  48.         }
  49.     }
  50.     
  51.     public static void main(String[] args) {
  52.         System.out.println(getSession());
  53.         close();
  54.     }
  55. }

步骤四:创建保持session在视图层开启的拦截器

在com.netctoss.interceptor包下,创建一个拦截器OpenSessionInViewInterceptor,在拦截方法中,先调用action和result,之后再关闭本次访问线程对应的session,代码如下:


    
    
  1. package com.netctoss.interceptor;
  2. import com.netctoss.util.HibernateUtil;
  3. import com.opensymphony.xwork2.ActionInvocation;
  4. import com.opensymphony.xwork2.interceptor.Interceptor;
  5. /**
  6. *    保持Session在视图层开启的拦截器,主要作用
  7. *    是在执行完JSP之后再统一关闭session。
  8. */
  9. public class OpenSessionInViewInterceptor
  10.     implements Interceptor {
  11.     @Override
  12.     public void destroy() {
  13.     }
  14.     @Override
  15.     public void init() {
  16.     }
  17.     @Override
  18.     public String intercept(ActionInvocation ai)
  19.             throws Exception {
  20.         // 调用action和result
  21.         ai.invoke();
  22.         /*
  23.          * result会把请求转发到页面,因此调用result,
  24.          * 就相当于调用JSP,因此此处的代码是在JSP之后执行。
  25.          * */
  26.         HibernateUtil.close();
  27.         return null;
  28.     }
  29. }

步骤五:注册并引用拦截器

在struts.xml中,注册并引用这个拦截器,代码如下:


    
    
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE struts PUBLIC
  3. "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
  4. "http://struts.apache.org/dtds/struts-2.1.7.dtd">
  5. <struts>
  6.     
  7.     <!-- 公共的包,封装了通用的拦截器、通用的result -->
  8.     <package name="netctoss" extends="json-default">
  9.         <interceptors>
  10.             <!-- 登录检查拦截器 -->
  11.             <interceptor name="loginInterceptor"
  12.                 class="com.netctoss.interceptor.LoginInterceptor"/>
  13.             <!-- 保持session开启拦截器 -->
  14.             <interceptor name="openSessionInterceptor"
  15.                 class="com.netctoss.interceptor.OpenSessionInViewInterceptor"/>
  16.             <!-- 登录检查拦截器栈 -->
  17.             <interceptor-stack name="loginStack">
  18.                 <interceptor-ref name="loginInterceptor"/>
  19.                 <interceptor-ref name="openSessionInterceptor"/>
  20.                 <!-- 不要丢掉默认的拦截器栈,里面有很多Struts2依赖的拦截器 -->
  21.                 <interceptor-ref name="defaultStack"/>
  22.             </interceptor-stack>
  23.         </interceptors>
  24.         <!-- 设置action默认引用的拦截器 -->
  25.         <default-interceptor-ref name="loginStack"/>
  26.         <!-- 全局的result,包下所有的action都可以共用 -->
  27.         <global-results>
  28.             <!-- 跳转到登录页面的result -->
  29.             <result name="login" type="redirectAction">
  30.                 <param name="namespace">/login</param>
  31.                 <param name="actionName">toLogin</param>
  32.             </result>
  33.         </global-results>
  34.     </package>
  35.     
  36.     <!--
  37.         资费模块配置信息:
  38.         一般情况下,一个模块的配置单独封装在一个package下,
  39.         并且以模块名来命名package的name和namespace。
  40.      -->
  41.     <package name="cost" namespace="/cost" extends="netctoss">
  42.         <!-- 查询资费数据 -->
  43.         <action name="findCost" class="com.netctoss.action.FindCostAction">
  44.             <!--
  45.                 正常情况下跳转到资费列表页面。
  46.                 一般一个模块的页面要打包在一个文件夹下,并且文件夹以模块名命名。
  47.              -->
  48.             <result name="success">
  49.                 /WEB-INF/cost/find_cost.jsp
  50.             </result>
  51.             <!--
  52.                 错误情况下,跳转到错误页面。
  53.                 错误页面可以被所有模块复用,因此放在main下,
  54.                 该文件夹用于存放公用的页面。
  55.              -->
  56.             <result name="error">
  57.                 /WEB-INF/main/error.jsp
  58.             </result>
  59.         </action>
  60.         <!-- 删除资费 -->
  61.         <action name="deleteCost"
  62.             class="com.netctoss.action.DeleteCostAction">
  63.             <!-- 删除完之后,重定向到查询action -->
  64.             <result name="success" type="redirectAction">
  65.                 findCost
  66.             </result>
  67.             <result name="error">
  68.                 /WEB-INF/main/error.jsp
  69.             </result>
  70.         </action>
  71.         <!-- 打开资费新增页 -->
  72.         <action name="toAddCost">
  73.             <result name="success">
  74.                 /WEB-INF/cost/add_cost.jsp
  75.             </result>
  76.         </action>
  77.         <!-- 资费名唯一性校验 -->
  78.         <action name="checkCostName"
  79.             class="com.netctoss.action.CheckCostNameAction">
  80.             <!-- 使用json类型的result把结果输出给回调函数 -->
  81.             <result name="success" type="json">
  82.                 <param name="root">info</param>
  83.             </result>
  84.         </action>
  85.         <!-- 打开修改页面 -->
  86.         <action name="toUpdateCost"
  87.             class="com.netctoss.action.ToUpdateCostAction">
  88.             <result name="success">
  89.                 /WEB-INF/cost/update_cost.jsp
  90.             </result>
  91.             <result name="error">
  92.                 /WEB-INF/main/error.jsp
  93.             </result>
  94.         </action>
  95.     </package>
  96.     
  97.     <!-- 登录模块 -->
  98.     <package name="login" namespace="/login" extends="struts-default">
  99.         <!--
  100.             打开登录页面:
  101.             1、action的class属性可以省略,省略时Struts2
  102.             会自动实例化默认的Action类ActionSupport,
  103.             该类中有默认业务方法execute,返回success。
  104.             2、action的method属性可以省略,省略时Struts2
  105.             会自动调用execute方法。
  106.         -->
  107.         <action name="toLogin">
  108.             <result name="success">
  109.                 /WEB-INF/main/login.jsp
  110.             </result>
  111.         </action>
  112.         <!-- 登录校验 -->
  113.         <action name="login" class="com.netctoss.action.LoginAction">
  114.             <!-- 校验成功,跳转到系统首页 -->
  115.             <result name="success">
  116.                 /WEB-INF/main/index.jsp
  117.             </result>
  118.             <!-- 登录失败,跳转回登录页面 -->
  119.             <result name="fail">
  120.                 /WEB-INF/main/login.jsp
  121.             </result>
  122.             <!-- 报错,跳转到错误页面 -->
  123.             <result name="error">
  124.                 /WEB-INF/main/error.jsp
  125.             </result>
  126.         </action>
  127.         <!-- 生成验证码 -->
  128.         <action name="createImage" class="com.netctoss.action.CreateImageAction">
  129.             <!-- 使用stream类型的result -->
  130.             <result name="success" type="stream">
  131.                 <!-- 指定输出的内容 -->
  132.                 <param name="inputName">imageStream</param>
  133.             </result>
  134.         </action>
  135.     </package>
  136.     
  137. </struts>

步骤六:重构资费DAO实现类,去掉关闭session

重构CostDaoImpl,将session关闭的代码注释掉,统一由拦截器关闭。重构后代码如下:


    
    
  1. package com.netctoss.dao;
  2. import java.util.List;
  3. import org.hibernate.HibernateException;
  4. import org.hibernate.Query;
  5. import org.hibernate.Session;
  6. import org.hibernate.Transaction;
  7. import com.netctoss.entity.Cost;
  8. import com.netctoss.util.HibernateUtil;
  9. /**
  10. *    当前阶段学习重点是Struts2,对于DAO的实现就模拟实现了。
  11. *    同学们可以使用JDBC/MyBatis自行实现该DAO。
  12. */
  13. public class CostDaoImpl implements ICostDao {
  14.     @Override
  15.     public List<Cost> findAll() {
  16.         String hql = "from Cost";
  17.         Session session = HibernateUtil.getSession();
  18.         Query query = session.createQuery(hql);
  19.         List<Cost> list = query.list();
  20. //        session.close();
  21.         return list;
  22.     }
  23.     @Override
  24.     public void delete(int id) {
  25.         Cost cost = new Cost();
  26.         cost.setId(id);
  27.         Session session = HibernateUtil.getSession();
  28.         Transaction ts = session.beginTransaction();
  29.         try {
  30.             session.delete(cost);
  31.             ts.commit();
  32.         } catch (HibernateException e) {
  33.             e.printStackTrace();
  34.             ts.rollback();
  35.         } finally {
  36. //            session.close();
  37.         }
  38.     }
  39.     @Override
  40.     public Cost findByName(String name) {
  41.         // 模拟根据名称查询资费数据,假设资费表中只有一条名为tarena的数据
  42.         if("tarena".equals(name)) {
  43.             Cost c = new Cost();
  44.             c.setId(97);
  45.             c.setName("tarena");
  46.             c.setBaseDuration(99);
  47.             c.setBaseCost(9.9);
  48.             c.setUnitCost(0.9);
  49.             c.setDescr("tarena套餐");
  50.             c.setStatus("0");
  51.             c.setCostType("2");
  52.             return c;
  53.         }
  54.         return null;
  55.     }
  56.     @Override
  57.     public Cost findById(int id) {
  58.         Session session = HibernateUtil.getSession();
  59.         Cost cost = (Cost) session.load(Cost.class, id);
  60. //        session.close();
  61.         return cost;
  62.     }
  63. }

步骤七:测试

重新部署项目,并启动tomcat,访问资费修改功能,效果如下图,可以看出使用了OpenSessionInViewInterceptor之后,的确解决了session提前关闭引发的延迟加载问题:

图-13

5.4 完整代码

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

其中CostDaoImpl完整代码如下:

HibernateUtil完整代码如下:

OpenSessionInViewInterceptor完整代码如下:

struts.xml完整代码如下:

6 使用一对多关联映射

6.1 问题

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

6.2 方案

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

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

6.3 步骤

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

步骤一:创建账务账号实体类

创建账务账号实体类Account,代码如下:


    
    
  1. package com.tarena.entity;
  2. import java.sql.Date;
  3. public class Account {
  4.     private Integer id;
  5.     private Integer recommenderId;
  6.     private String loginName;
  7.     private String loginPassword;
  8.     private String status;
  9.     private Date createDate;
  10.     private Date pauseDate;
  11.     private Date closeDate;
  12.     private String realName;
  13.     private String idcardNo;
  14.     private Date birthdate;
  15.     private String gender;
  16.     private String occupation;
  17.     private String telephone;
  18.     private String email;
  19.     private String mailaddress;
  20.     private String zipcode;
  21.     private String qq;
  22.     private Date lastLoginTime;
  23.     private String lastLoginIp;
  24.     
  25.     public Integer getId() {
  26.         return id;
  27.     }
  28.     public void setId(Integer id) {
  29.         this.id = id;
  30.     }
  31.     public Integer getRecommenderId() {
  32.         return recommenderId;
  33.     }
  34.     public void setRecommenderId(Integer recommenderId) {
  35.         this.recommenderId = recommenderId;
  36.     }
  37.     public String getLoginName() {
  38.         return loginName;
  39.     }
  40.     public void setLoginName(String loginName) {
  41.         this.loginName = loginName;
  42.     }
  43.     public String getLoginPassword() {
  44.         return loginPassword;
  45.     }
  46.     public void setLoginPassword(String loginPassword) {
  47.         this.loginPassword = loginPassword;
  48.     }
  49.     public String getStatus() {
  50.         return status;
  51.     }
  52.     public void setStatus(String status) {
  53.         this.status = status;
  54.     }
  55.     public Date getCreateDate() {
  56.         return createDate;
  57.     }
  58.     public void setCreateDate(Date createDate) {
  59.         this.createDate = createDate;
  60.     }
  61.     public Date getPauseDate() {
  62.         return pauseDate;
  63.     }
  64.     public void setPauseDate(Date pauseDate) {
  65.         this.pauseDate = pauseDate;
  66.     }
  67.     public Date getCloseDate() {
  68.         return closeDate;
  69.     }
  70.     public void setCloseDate(Date closeDate) {
  71.         this.closeDate = closeDate;
  72.     }
  73.     public String getRealName() {
  74.         return realName;
  75.     }
  76.     public void setRealName(String realName) {
  77.         this.realName = realName;
  78.     }
  79.     public String getIdcardNo() {
  80.         return idcardNo;
  81.     }
  82.     public void setIdcardNo(String idcardNo) {
  83.         this.idcardNo = idcardNo;
  84.     }
  85.     public Date getBirthdate() {
  86.         return birthdate;
  87.     }
  88.     public void setBirthdate(Date birthdate) {
  89.         this.birthdate = birthdate;
  90.     }
  91.     public String getGender() {
  92.         return gender;
  93.     }
  94.     public void setGender(String gender) {
  95.         this.gender = gender;
  96.     }
  97.     public String getOccupation() {
  98.         return occupation;
  99.     }
  100.     public void setOccupation(String occupation) {
  101.         this.occupation = occupation;
  102.     }
  103.     public String getTelephone() {
  104.         return telephone;
  105.     }
  106.     public void setTelephone(String telephone) {
  107.         this.telephone = telephone;
  108.     }
  109.     public String getEmail() {
  110.         return email;
  111.     }
  112.     public void setEmail(String email) {
  113.         this.email = email;
  114.     }
  115.     public String getMailaddress() {
  116.         return mailaddress;
  117.     }
  118.     public void setMailaddress(String mailaddress) {
  119.         this.mailaddress = mailaddress;
  120.     }
  121.     public String getZipcode() {
  122.         return zipcode;
  123.     }
  124.     public void setZipcode(String zipcode) {
  125.         this.zipcode = zipcode;
  126.     }
  127.     public String getQq() {
  128.         return qq;
  129.     }
  130.     public void setQq(String qq) {
  131.         this.qq = qq;
  132.     }
  133.     public Date getLastLoginTime() {
  134.         return lastLoginTime;
  135.     }
  136.     public void setLastLoginTime(Date lastLoginTime) {
  137.         this.lastLoginTime = lastLoginTime;
  138.     }
  139.     public String getLastLoginIp() {
  140.         return lastLoginIp;
  141.     }
  142.     public void setLastLoginIp(String lastLoginIp) {
  143.         this.lastLoginIp = lastLoginIp;
  144.     }
  145. }

步骤二:创建账务账号映射关系文件

创建账务账号映射关系文件Account.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.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.     </class>
  51. </hibernate-mapping>

步骤三:创建业务账号实体类

创建业务账号实体类Service,代码如下:


    
    
  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.     public Integer getId() {
  15.         return id;
  16.     }
  17.     public void setId(Integer id) {
  18.         this.id = id;
  19.     }
  20.     public Integer getAccountId() {
  21.         return accountId;
  22.     }
  23.     public void setAccountId(Integer accountId) {
  24.         this.accountId = accountId;
  25.     }
  26.     public String getUnixHost() {
  27.         return unixHost;
  28.     }
  29.     public void setUnixHost(String unixHost) {
  30.         this.unixHost = unixHost;
  31.     }
  32.     public String getOsUserName() {
  33.         return osUserName;
  34.     }
  35.     public void setOsUserName(String osUserName) {
  36.         this.osUserName = osUserName;
  37.     }
  38.     public String getLoginPassword() {
  39.         return loginPassword;
  40.     }
  41.     public void setLoginPassword(String loginPassword) {
  42.         this.loginPassword = loginPassword;
  43.     }
  44.     public String getStatus() {
  45.         return status;
  46.     }
  47.     public void setStatus(String status) {
  48.         this.status = status;
  49.     }
  50.     public Date getCreateDate() {
  51.         return createDate;
  52.     }
  53.     public void setCreateDate(Date createDate) {
  54.         this.createDate = createDate;
  55.     }
  56.     public Date getPauseDate() {
  57.         return pauseDate;
  58.     }
  59.     public void setPauseDate(Date pauseDate) {
  60.         this.pauseDate = pauseDate;
  61.     }
  62.     public Date getCloseDate() {
  63.         return closeDate;
  64.     }
  65.     public void setCloseDate(Date closeDate) {
  66.         this.closeDate = closeDate;
  67.     }
  68.     public Integer getCostId() {
  69.         return costId;
  70.     }
  71.     public void setCostId(Integer costId) {
  72.         this.costId = costId;
  73.     }
  74. }

步骤四:创建业务账号映射关系文件

创建业务账号映射关系文件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.         <property name="accountId"
  12.             type="integer" column="ACCOUNT_ID"/>
  13.         <property name="unixHost"
  14.             type="string" column="UNIX_HOST"/>
  15.         <property name="osUserName"
  16.             type="string" column="OS_USERNAME"/>
  17.         <property name="loginPassword"
  18.             type="string" column="LOGIN_PASSWD"/>
  19.         <property name="status"
  20.             type="string" column="STATUS"/>
  21.         <property name="createDate"
  22.             type="date" column="CREATE_DATE"/>
  23.         <property name="pauseDate"
  24.             type="date" column="PAUSE_DATE"/>
  25.         <property name="closeDate"
  26.             type="date" column="CLOSE_DATE"/>
  27.         <property name="costId"
  28.             type="integer" column="COST_ID"/>
  29.     </class>
  30. </hibernate-mapping>

步骤五:声明映射关系文件

在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.         <mapping resource="com/tarena/entity/Emp.hbm.xml" />
  30.         <mapping resource="com/tarena/entity/Account.hbm.xml" />
  31.         <mapping resource="com/tarena/entity/Service.hbm.xml" />        
  32.     </session-factory>
  33. </hibernate-configuration>

步骤六:在账务账号实体类中追加集合属性

在账务账号实体类Account中,追加集合属性,用于封装它对应的所有业务账号,代码如下:


    
    
  1. package com.tarena.entity;
  2. import java.sql.Date;
  3. import java.util.Set;
  4. public class Account {
  5.     private Integer id;
  6.     private Integer recommenderId;
  7.     private String loginName;
  8.     private String loginPassword;
  9.     private String status;
  10.     private Date createDate;
  11.     private Date pauseDate;
  12.     private Date closeDate;
  13.     private String realName;
  14.     private String idcardNo;
  15.     private Date birthdate;
  16.     private String gender;
  17.     private String occupation;
  18.     private String telephone;
  19.     private String email;
  20.     private String mailaddress;
  21.     private String zipcode;
  22.     private String qq;
  23.     private Date lastLoginTime;
  24.     private String lastLoginIp;
  25.     // 追加关联属性,用于存储相关的Service信息
  26.     private Set<Service> services;
  27.     public Set<Service> getServices() {
  28.         return services;
  29.     }
  30.     public void setServices(Set<Service> services) {
  31.         this.services = services;
  32.     }
  33.     public Integer getId() {
  34.         return id;
  35.     }
  36.     public void setId(Integer id) {
  37.         this.id = id;
  38.     }
  39.     public Integer getRecommenderId() {
  40.         return recommenderId;
  41.     }
  42.     public void setRecommenderId(Integer recommenderId) {
  43.         this.recommenderId = recommenderId;
  44.     }
  45.     public String getLoginName() {
  46.         return loginName;
  47.     }
  48.     public void setLoginName(String loginName) {
  49.         this.loginName = loginName;
  50.     }
  51.     public String getLoginPassword() {
  52.         return loginPassword;
  53.     }
  54.     public void setLoginPassword(String loginPassword) {
  55.         this.loginPassword = loginPassword;
  56.     }
  57.     public String getStatus() {
  58.         return status;
  59.     }
  60.     public void setStatus(String status) {
  61.         this.status = status;
  62.     }
  63.     public Date getCreateDate() {
  64.         return createDate;
  65.     }
  66.     public void setCreateDate(Date createDate) {
  67.         this.createDate = createDate;
  68.     }
  69.     public Date getPauseDate() {
  70.         return pauseDate;
  71.     }
  72.     public void setPauseDate(Date pauseDate) {
  73.         this.pauseDate = pauseDate;
  74.     }
  75.     public Date getCloseDate() {
  76.         return closeDate;
  77.     }
  78.     public void setCloseDate(Date closeDate) {
  79.         this.closeDate = closeDate;
  80.     }
  81.     public String getRealName() {
  82.         return realName;
  83.     }
  84.     public void setRealName(String realName) {
  85.         this.realName = realName;
  86.     }
  87.     public String getIdcardNo() {
  88.         return idcardNo;
  89.     }
  90.     public void setIdcardNo(String idcardNo) {
  91.         this.idcardNo = idcardNo;
  92.     }
  93.     public Date getBirthdate() {
  94.         return birthdate;
  95.     }
  96.     public void setBirthdate(Date birthdate) {
  97.         this.birthdate = birthdate;
  98.     }
  99.     public String getGender() {
  100.         return gender;
  101.     }
  102.     public void setGender(String gender) {
  103.         this.gender = gender;
  104.     }
  105.     public String getOccupation() {
  106.         return occupation;
  107.     }
  108.     public void setOccupation(String occupation) {
  109.         this.occupation = occupation;
  110.     }
  111.     public String getTelephone() {
  112.         return telephone;
  113.     }
  114.     public void setTelephone(String telephone) {
  115.         this.telephone = telephone;
  116.     }
  117.     public String getEmail() {
  118.         return email;
  119.     }
  120.     public void setEmail(String email) {
  121.         this.email = email;
  122.     }
  123.     public String getMailaddress() {
  124.         return mailaddress;
  125.     }
  126.     public void setMailaddress(String mailaddress) {
  127.         this.mailaddress = mailaddress;
  128.     }
  129.     public String getZipcode() {
  130.         return zipcode;
  131.     }
  132.     public void setZipcode(String zipcode) {
  133.         this.zipcode = zipcode;
  134.     }
  135.     public String getQq() {
  136.         return qq;
  137.     }
  138.     public void setQq(String qq) {
  139.         this.qq = qq;
  140.     }
  141.     public Date getLastLoginTime() {
  142.         return lastLoginTime;
  143.     }
  144.     public void setLastLoginTime(Date lastLoginTime) {
  145.         this.lastLoginTime = lastLoginTime;
  146.     }
  147.     public String getLastLoginIp() {
  148.         return lastLoginIp;
  149.     }
  150.     public void setLastLoginIp(String lastLoginIp) {
  151.         this.lastLoginIp = lastLoginIp;
  152.     }
  153. }

步骤七:在账务账号映射关系文件中配置集合属性

在账务账号映射关系文件Account.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.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.             <!-- 用于指定关联条件,写关联条件的外键字段 -->
  53.             <key column="ACCOUNT_ID"/>
  54.             <!-- 用于指定采用哪种关系,加载哪方数据 -->
  55.             <one-to-many class="com.tarena.entity.Service"/>
  56.         </set>
  57.     </class>
  58. </hibernate-mapping>

步骤八:创建测试类

在com.tarena.test包下创建一个测试类TestOneToMany,并在这个类中增加一个测试方法,查询出某条账务账号的数据,然后输出账务账号以及账务账号中追加的集合属性值,代码如下:


    
    
  1. package com.tarena.test;
  2. import java.util.Set;
  3. import org.hibernate.Session;
  4. import org.junit.Test;
  5. import com.tarena.entity.Account;
  6. import com.tarena.entity.Service;
  7. import com.tarena.util.HibernateUtil;
  8. public class TestOneToMany {
  9.     
  10.     @Test
  11.     public void test1() {
  12.         Session session = HibernateUtil.getSession();
  13.         Account account =
  14.                 (Account) session.get(Account.class, 1011);
  15.         System.out.println(
  16.                 account.getIdcardNo() + " , "
  17.                 + account.getRealName() + " , "
  18.                 + account.getBirthdate());
  19.         
  20.         System.out.println("------------------");
  21.         
  22.         Set<Service> services = account.getServices();
  23.         System.out.println(services.getClass().getName());
  24.         for (Service service : services) {
  25.             System.out.println(service.getOsUserName());
  26.         }
  27.         session.close();
  28.     }
  29.     
  30. }

步骤九:测试

执行这个方法,控制台输出结果如下图,可以看出查询账务账号之后,Hibernate自动查询出了SERVICE数据并封装到了services属性中,并且对于SERVICE的查询是采用延迟加载机制的。

图-14

6.4 完整代码

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

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

账务账号映射关系文件Account.hbm.xml完整代码如下:

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

业务账号实体类Service.hbm.xml完整代码如下:

主配置文件hibernate.cfg.xml完整代码如下:

测试类TestOneToMany完整代码如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值