Home Hibernate笔记
Post
Cancel

Hibernate笔记

一、JavaProject配置过程(TestHibernate)

1、加入Hibernate支持,即拷贝Hibernate支持jar包;创建hiberante.cfg.xml文件。(注解配置方式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- 数据库连接 -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/bbs_db</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
       
        <!-- 方言dialect -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
       
        <!-- 是否显示sql语句 -->
        <property name="show_sql">true</property>
       
        <!-- 数据库更新方式 -->
        <property name="hbm2ddl.auto">update</property>
       
        <!-- 实体关系映射 -->
        <mapping class="com.bbs.bean.User"/>       
    </session-factory>
</hibernate-configuration>

2、创建持久化类Admin、User等,并通过注解配置“实体关系映射”。(重点)

  • 涉及多个知识点,如:@Many To One映射关系,@One To Many等。(重点)
  • @GeneratedValue主键生成策略,常用strategy = GenerationType.AUTO(重点)
  • fetch=FetchType.LAZY懒加载设置及级联配置cascade设置。(重点)(此处为系统设计部分,初级工程师先关注具体使用。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package com.bbs.bean;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
 
// 用户注册实体类,对应t_user表
@Entity
@Table(name = "t_user")
public class User {
    private int userid;
    private String username;
    private String password;
    private String sex;
    private String hobbys;
    private String birthday;
    private String city;
    private String email;
    private String qq;
    private String createtime;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public int getUserid() {
      return userid;
    }

    @Column(name = "username")
    public String getUsername() {
      return username;
    }

    @Column(name = "password")
    public String getPassword() {
      return password;
    }

    @Column(name = "sex")
    public String getSex() {
      return sex;
    }

    @Column(name = "hobbys")
    public String getHobbys() {
      return hobbys;
    }

    @Column(name = "birthday")
    public String getBirthday() {
      return birthday;
    }

    @Column(name = "city")
    public String getCity() {
      return city;
    }

    @Column(name = "email")
    public String getEmail() {
      return email;
    }

    @Column(name = "qq")
    public String getQq() {
      return qq;
    }

    @Column(name = "createtime")
    public String getCreatetime() {
      return createtime;
    }

    public void setUserid(int userid) {
      this.userid = userid;
    }

    public void setUsername(String username) {
      this.username = username;
    }

    public void setPassword(String password) {
      this.password = password;
    }

    public void setSex(String sex) {
      this.sex = sex;
    }

    public void setHobbys(String hobbys) {
      this.hobbys = hobbys;
    }

    public void setBirthday(String birthday) {
      this.birthday = birthday;
    }

    public void setCity(String city) {
      this.city = city;
    }

    public void setEmail(String email) {
      this.email = email;
    }

    public void setQq(String qq) {
      this.qq = qq;
    }

    public void setCreatetime(String createtime) {
      this.createtime = createtime;
    }
 
}

3、JavaProject工程项目下,需要通过工厂类,获取session对象:(代码片段)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.bbs.util;
 
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
 
/**
 * 自定义工厂类 单例模式
 * Hibernate五大核心接口
 * configuration接口、
 * sessionFactory接口、
 * session接口、
 * Transaction接口、
 * query接口
 */
public class MySessionFactory {
    /**
      * 1.设置私有的构造函数
      * 2.定义一个静态变量
      * 3.定义一段静态代码块
      * 4.定义一个方法用于获取单例模式对象
      * 5.单例模式分为饿汉模式和懒汉模式两种
      */
    private static SessionFactory sessionFactory;

    static {
        // 使用configuration接口获取hibernate配置文件 /hibernate.cfg.xml"
        // 第一种读取注解,第二种关系映射配置文件形式
        Configuration configuration = new AnnotationConfiguration();
        configuration.configure("hibernate.cfg.xml");
        // 获取工厂类
        if (sessionFactory == null) {
            sessionFactory = configuration.buildSessionFactory();
        }
    }

    // 单例模式下具有一个私有的构造函数
    private MySessionFactory() {

    }

    // 该方法用于获取工厂类
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

}

4、调用HibernateAPI五大接口,SessionFactory、Session、Transaction,完成对象持久化过程,即保存操作。

1
2
3
4
5
6
7
8
9
10
11
12
SessionFactory factory = MySessionFactory.getSessionFactory();
Session session = factory.openSession();
// 开启事务
session.beginTransaction();
Admin admin = new Admin();
admin.setAdminname("zhangsan");
admin.setAdminpassword("123456");
// 临时状态(瞬时状态)Transient
session.save(admin);
// 提交事务
 session.getTransaction().commit();
 session.close();

及Query接口,HQL语句执行。

1
2
3
4
Session session = factory.openSession();
Query q = session.createQuery("from Admin e where e.adminid=:id");
q.setInteger("id", 1);
session.close();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.bbs.test;
 
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.bbs.bean.User;
import com.bbs.util.MySessionFactory;
 
public class TestOpenSession {
    public static void main(String[] args) {
        SessionFactory factory = MySessionFactory.getSessionFactory();
        Session session = factory.openSession();
        // 开启事务
        session.beginTransaction();
        // 临时对象
        User user = new User();
        user.setCreatetime("1995-15-15");
        user.setPassword("123");
        user.setSex("1");
        user.setUsername("zhong");
        // 临时状态
        // 持久化过程就是一个数据保存的过程
        session.save(user);
        // 提交事务
        session.getTransaction().commit();
        session.close();
        
        //执行HQL语句
        Session session1 = factory.openSession();
        Query q = session1.createQuery("from User u where u.userid=:id");
        q.setInteger("id", 8);
        User user1 = (User) session1.get(User.class, 8);
        System.out.println("1." + ((User)(q.list().get(0))).getUsername());
        System.out.println("2." + user1.getUsername());
        session1.close();
    }

}

二、真实项目使用情况(TestHibernate1)

对以上项目架构做分析,其中factory.openSession()的使用,在实际项目中,每次访问都产生一个session,假如连续使用多次,则获得的session不是同一个对象,并且使用完需要调用close方法关闭session(每次都需要关闭;增加服务器负担),所以,我们通常使用factory.getCurrentSession,从字面上可以看得出来,是获取当前上下文一个session对象,当第一次使用此方法时,会自动产生一个session对象,并且连续使用多次时,得到的session都是同一个对象,这就是与openSession的区别之一,简单而言,getCurrentSession就是,如果有已经使用的,用旧的,如果没有,建新的。getCurrentSession是在事务提交时自动调用close方法。实际项目中,我们选择使用getCurrentSession,并且此方法的使用,必须开启事务。使用SessionFactory.getCurrentSession()需要在hibernate.cfg.xml中如下配置:

  • 如果采用jdbc独立引用程序配置如下

    1
    
    <property name="hibernate.current_session_context_class">thread</property>
    
  • 如果采用了JTA事务配置如下

    1
    
    <property name="hibernate.current_session_context_class">jta</property>
    

具体使用步骤,如下:

  1. 需要上下文配置(即在hibernate.cfg.xml)中,需要配置

    1
    
     <property name="current_session_context_class">thread</property>
    
  2. 代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    
     // 必须加入事务,线程安全
     Session session = factory.getCurrentSession();
     // 开启事务
     session.beginTransaction();
     Message e = (Message) session.get(Message.calss, 39);
    
     // 提交 自动close
     session.getTransaction().commit();
    

    小提示:事务的处理,可以通过动态代理简化代码量,并可统一管理。

在实际项目中,我们还需要考虑数据库访问时效性。需要使用“JDBC连接池”。我们选择C3P0,具体配置如下,同时导入C3P0的jar包支持:

1
2
<!-- 连接池Jdbc connection pool C3P0 -->
<property name="connection.pool_size">1</property>

三、WebProject配置过程(TestSH1.1&TestSH1.2)

Hibernate的使用我们在JavaProject中已经充分展示了;在WebProject项目中,使用Hibernate的方式和Java项目中没有太多区别。我们在实际项目中,是通过Spring整合Hibernate的方式集成使用;总体来说写的代码绝对比单独用Hibernate然后在DAO类里写的代码要少;使用Spring整合Hibernate,配置较多。

使用getCurrentSession,所有的操作都必须开启事务,所以需要我们注意,使用Spring框架的声明式事务,不再需要自动创建sessionFactory和session,不再需要手动控制事务的开启和关闭。(重点)

具体实现步骤如下:

  1. 在Spirng配置文件中,装配一个org.springframework.orm.hibernate3.LocalSessionFactoryBean类。
    • 方法一、配置数据源,读取配置文件,获取连接信息;设置hibernate配置信息。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
     <bean id="dataSource" destory-method="close" 
         class="org.apache.commons.dbcp.BasicDataSource">
         <property name="driverClassName" value="${jdbc.driverClassName}"/>
         <property name="url" value="${jdbc.url}"/>
         <property name="username" value="${jdbc.username}"/>
         <property name="password" value="${jdbc.password}"/>
     </bean>
     <bean id="sessionFactory" 
         class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
         <property name="dataSource" ref="dataSource"/>
         <property name="annotatedClasses">
             <list>
                 <value>com.hp.model.User</value>
                 <value>com.hp.model.Log</value>
             </list>
         </property>
         <property name="hibernateProperties">
             <props>
                 <prop key="hibernate.dialect">
                   org.hibernate.dialect.OracleDialect
                 <prop>
                 <prop key="hibernate.show_sql">true</prop>
             </props>
         </property>
     </bean>
    

    或者hibernate3版本的AnnotationSessionFactoryBean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
     <bean id="sessionFactory" 
      class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
         <property name="dataSource">
             <ref bean="dataSource"/>
         </property>
         <property name="hibernateProperties">
             <props>
                 <prop key="hibernate.dialect">
                     org.hibernate.dialect.MySQLDialect
                 </prop>
                 <!-- 加入事务,就不需要此处配置 -->
                 <prop key="hibernate.current_session_context_class">thread</prop>
                 <prop key="hibernate.show_sql">true</prop>
             </props>
         </property>
         <property name="packagesToScan">
             <list>
                 <value>com.bbs.bean</value>
             </list>
         </property>
     </bean>
    
  • 方法二、可以通过configLocation属性,加载hibernate.cfg.xml文件。

    1
    2
    3
    4
    5
    
    <bean id="sessionFactory" 
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="configLocation" value="file:src/hibernate.cfg.xml">
        </property>
    </bean>
    
    1
    
        2. 在spring环境下,新建一个操作类TestSpringDao,需要继承HibernateDaoSupport。
    
    1
    2
    3
    4
    5
    6
    
    public void test1() {
        Session session = this.getSessionFactory().getCurrentSession();
        session.beginTransaction();
        Message e = (Message) session.get(Message.class, 35);
        session.getTransaction().commit();
    }
    
  1. 在spring配置文件中,装配一个bean,必须注入属性sessioniFactory值。

    1
    2
    3
    4
    5
    
     <bean id="testDao1" class="com.bbs.test.TestSpringDao">
         <property name="sessionFactory">
             <ref bean="sessionFactory"/>
         </property>
     </bean>
    
  2. 单元测试,此时可以获取Message对象,才成功。

    1
    2
    3
    4
    5
    
     ApplicationContext ac = 
         new ClassPathXmlApplicationContext("applicationContext.xml");
     // 如何获取类的实例
     TestSpringDao dao1 = (TestSpringDao) ac.getBean("testDao1");
     dao1.test1();
    

四、实际项目使用(TestSH2)

在Spring整合Hibernate框架中,方法中需要开启事务,以及提交事务。这部分代码基本都是模板化的,那么,我们如何在编写过程中把这部分代码省略,或者让系统自动为我们添加。这时,我们考虑到动态代理模式及SpringAOP。我们在实际项目中是如何做的?demo演示如下,在实际项目中我们选择Spring3.0及Hibernate3.0,我们选择注解的形式:

  1. 首先从配置文件开始:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
     <!-- apache下的BasicDataSource,数据库连接池 -->
     <bean id="dataSource" destory-method="close" 
         class="org.apache.commons.dbcp.BasicDataSource">
         <property name="driverClassName">
             <value>com.mysql.jdbc.Driver</value>
         </property>
         <property name="url" value="${jdbc.url}"/>
         <property name="username" value="root"/>
         <property name="password" value="root"/>
     </bean>
     <bean id="sessionFactory" 
      class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
         <property name="dataSource">
             <ref bean="dataSource"/>
         </property>
         <property name="hibernateProperties">
             <props>
                 <prop key="hibernate.dialect">
                     org.hibernate.dialect.MySQLDialect
                 </prop>
                 <!-- 加入事务,就不需要此处配置 -->
                 <prop key="hibernate.current_session_context_class">thread</prop>
                 <prop key="hibernate.show_sql">true</prop>
             </props>
         </property>
         <property name="packagesToScan">
             <list>
                 <value>com.bbs.bean</value>
             </list>
         </property
     </bean>
    
  2. 设计dao层具体实现:

    按照MVC的设计思想,我们创建UserDao层,com.bbs.dao.impl,创建类UserDaoImpl,继承HibernateDaoSupport,这个类是Spring框架提供的,我们使用其中的getHibernateTemplate方法(其中还有一个getSession方法,不常用),getHibernateTemplate方法提供非常多的常用方法来完成基本的操作。而getSession方法是没有经过Spring包装的,Spring会把最原始的Session给你,在使用完之后必须自己调用相应的close方法,而且也不会对声明式事务进行相应的管理,一旦没有及时关闭连接,就会导致数据库连接池的连接数溢出;getHibernateTemplate()方法是经过Spring封装的,例如添加相应的声明式事务管理,由Spring管理相应的连接。

    在实际的使用过程中发现的确getHibernateTemplate()比getSession()方法要好很多,但是有些方法在getHibernateTemplate()并没有提供,这时我们用HibernateCallback回调的方法操作数据库(比如对原生sql语句的执行)。

    继承HibernateDaoSupport是可以的,但这种方式还是不太好,还是用到了继承,也就是没有降低所谓的耦合度。解决方案:

    • Spring为我们提供了一个hibernateTemplate,你只需在配置文件中配置一个bean,并传入一个sessionFactory。

      1
      2
      3
      4
      
      <bean id="hibernateTemplate" 
          class="com.springframework.orm.hibernate3.HibernateTemplate">
          <property name="sessionFactory" ref="sessionFactory"/>
      </bean>
      
    • 然后在使用dao的时候注入该hibernateTemplate的bean即可。通过注解实现,因此@Component相当于<bean id="" class=""/>

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      
      @Component
      public class UserDaoImpl implements UserDao {
          @Autowired
          private HibernateTemplate hibernateTemplate;
      
          public HibernateTemplate getHibernateTemplate() {
              return hibernateTemplate;
          }
      
          public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
              this.hibernateTemplate = hibernateTemplate;
          }
      }
      
    • 在方法中直接使用hibernateTemplate提供的get、load、find等方法。

  3. 事务管理,Spring提供的事务管理可以分为两类:编程式、声明式,编程式,其实就是在代码里面来控制,像Hibernate操作数据一样,开启事务,提交事务,这种方式有一定的局限性,所以我们一般是用声明式来配置我们的事务。在Spring和Hibernate整合架构中,声明式事务配置,包括以下三项:
    • 事务管理类,我们选择DataSourceTransactionManager或HibernateTransactionManager;
    • 以及声明式事务隔离级别及传播特性,我们通过advice通知拦截;(其中包括五部分,重点,难点)
      传播行为

      隔离级别 isolation=”default”
      1
      2
      
      <tx:method name="add" propagation="REQUIRED" 
          isolation="default" read-only="false"/>
      


      只读 read-only=”false”

      事务超时 timeout=”-1”

      1
      
      <tx:method name="add" timeout="-1">
      

      回滚规则 rollback-for=”” / no-rollback-for=””

      1
      
      <tx:method name="update" no-rollback-for="">
      
    • 以及哪些类和方法参与事务,我们通过SpringAOP实现。

    经过以上配置,我们可以实现特定的业务,执行不同的事务策略。程序员只需要关心业务代码实现即可,极大的减轻了程序员额外代码。当然,事务切入点,我们是在sevice层去配置。

  4. Spring配置中定义扫描器,把这些类,纳入Spring容器管理中。

    1
    2
    
     <!-- 扫描注解类 -->
     <context:component-scan base-package="com.bbs"/>
    
  5. 至此,我们的整合工作就基本完成了。我们可以通过一个单元测试,验证一下:

    1
    2
    3
    4
    5
    6
    
     ApplicationContext ac = 
             new ClassPathXmlApplicationContext("applicationContext.xml");
     // 如何获取类的实例
     UserDao dao1 = (UserDao) ac.getBean("userDaoImpl");
     User user = dao1.findUserById(9);
     System.out.println(user.getUsername());
    
  6. 业务层service改造,及注解实现:

    • @Service用于标注业务层组件,即service层类
    • @Controller用于标注控制层组件(如spingMVC中的控制类)
    • @Repository用于标注数据访问组件,即dao层类
    • @Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      
       @Service
       public class UserService implements IuserService {
         @Autowired
         private IUserDao userDao;
      
         public IUserDao getUserDao() {
           return userDao;
         }
      
         public void setUserDao(IUserDao userDao) {
           this.userDao = userDao;
         }
       }
      
  7. 控制层改造及实现,及代码片段:使用@controller注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
     @Controller
     public class LoginServlet {
       @Autowired
       IUserService userService;
    
       public IUserService getUserService() {
         return userService;
       }
    
       public void setUserService(IUserService userService) {
         this.userService = uerService;
       }
       @RequestMapping(value="login", method=RequestMethod.POST)
     }
    

五、BBS论坛项目,升级为SpringMVC3.0 + Hibernate3.0 + Mysql5.1 + DBCP数据源项目,升级项目名SH_BBSV4.0

具体实现步骤,参考以为配置。

六、Hibernate二级缓存

  • 一级缓存及Session缓存,是事务级别的,由Hibernate去管理,一般不去干涉。
  • 二级缓存是sessionFactroy缓存,我们可以根据业务访问及修改频次去设置缓存级别,及设置缓存文字。
This post is licensed under CC BY 4.0 by the author.