zoukankan      html  css  js  c++  java
  • 三种数据库访问——Spring3.2 + Hibernate4.2

    前三篇随笔中介绍了 用原生的JDBC访问数据库一种高效的数据库连接池druid用Spring的JDBC框架访问数据库

    本文继续介绍第三种数据库访问的解决方案:Spring3.2 + Hibernate4.2

    ORM框架

    Hibernate是一个开源的ORM框架,能自动为对象生成相应SQL并透明的持久化对象到数据库,我们首先来了解一下什么是“ORM”。

    ORM全称对象关系映射(Object/Relation Mapping),指将Java对象状态自动映射到关系数据库中的数据上,从而提供透明化的持久化支持,即把一种形式转化为另一种形式。

    对象与关系数据库之间是不匹配,我们把这种不匹配称为阻抗失配,主要表现在:

    1. 关系数据库首先不支持面向对象技术,如继承、多态;
    2. 关系数据库是由表来存放数据,而面向对象使用对象来存放状态;
    3. 如何将对象透明的持久化到关系数据库表中;
    4. 如果一个对象存在横跨多个表的数据,应该有一种策略来对象建模和映射。


    其中这些阻抗失配只是其中的一小部分,比如还有如何将SQL集合函数结果集映射到对象,如何在对象中处理主键等。ORM框架就是用来解决这种阻抗失配,提供关系数据库的对象化支持。

    目前已经有许多ORM框架产生,如Hibernate、JDO、JPA、iBATIS等等,这些ORM框架各有特色,Spring对这些ORM框架提供了很好的支持。

    示例项目

    接下来,我们还是通过一个实际的项目实践Spring+Hibernate框架访问数据库。假设该项目的功能有:保存用户信息、查询用户信息。

    1、工程结构和依赖项

    这是一个Maven工程,工程结构、依赖的配置如下:

        <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.26</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>0.2.25</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>3.2.4.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-orm</artifactId>
                <version>3.2.4.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-core</artifactId>
                <version>4.2.4.Final</version>
            </dependency>

    依赖的项目有:Junit;mysql驱动;druid:一款高效的数据库连接池,下面会看到如何应用它;spring-context;spring-orm;hibernate-core

    2、数据库表及实体类

    CREATE TABLE `user` (
      `id` int(10) NOT NULL auto_increment,
      `name` varchar(30) default NULL,
      `age` int(3) default NULL,
      PRIMARY KEY  (`id`)
    )
    import java.io.Serializable;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    @Entity
    @Table(name="user")
    public class User implements Serializable{
        private static final long serialVersionUID = 1724315528421427938L;
        
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name = "id", nullable = false)   
        private Long id;
        @Column(name="name",nullable=true,length=25)
        private String name;
        @Column(name="age",nullable=true)
        private Integer age;
        
        //setter getter 略
    
        @Override
        public String toString() {
            return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
        }
    }

    这个Pojo类和上面的数据库表对应,我们采用JPA注解来配置这种对应关系。(JPA是一种规范,通过JDK 5.0注解或XML描述对象-关系表的映射关系)。

    3、配置数据库连接池

    applicationContext-dataSource.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
        http://www.springframework.org/schema/beans        
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context                
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
            init-method="init" destroy-method="close">
            <property name="url" value="jdbc:mysql://localhost:3306/test" />
            <property name="username" value="root" />
            <property name="password" value="123456" />
            <property name="initialSize" value="1" />
            <property name="maxActive" value="20" />
        </bean>
    
    </beans>

    在这,我应用了阿里巴巴开发的一个高效的数据库连接池:druid。

    阿里巴巴官网上介绍,druid是目前最好的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。

    与druid相关的详细内容可以看我之前的一篇随笔。

    4、配置SessonFactory、配置事务处理器、配置bean的依赖关系

    applicationContext-hibernate.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
        <import resource="applicationContext-dataSource.xml" />
    
        <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="packagesToScan" value="edu.shao.springHibernate.po" />
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                    <prop key="hibernate.show_sql">true</prop>
                    <prop key="hibernate.format_sql">false</prop>
                    <prop key="hibernate.use_sql_comments">false</prop>
                    <prop key="hibernate.cache.use_second_level_cache">false</prop>
                </props>
            </property>
        </bean>
        
        <bean id="userService" class="edu.shao.springHibernate.service.impl.UserService">
            <property name="userDao">
                <bean class="edu.shao.springHibernate.dao.impl.UserDaoImpl">
                    <property name="sessionFactory" ref="sessionFactory"></property>
                </bean>
            </property>
        </bean>
     
        <!-- 事务管理器 -->  
        <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">  
            <property name="sessionFactory" ref="sessionFactory"/>  
        </bean> 
        
        <tx:annotation-driven transaction-manager="transactionManager"/>
    
    </beans>

    在这里用的是基于注解的事务配置方式。用注解的好处,是Service中的方法命名不需要特别规定(基于xml的事务配置方式需要Service的方法命名按照某一自定义规范命名),只需要在Service类上或方法上加上@Transactional注解即可;缺点是没有做到集中声明,如果在某个Service层的接口忘记声明事务,那么事务就无法生效。

    <tx:annotation-driven transaction-manager="txManager" /> 这句话的作用是注册事务注解处理器。

    如果方法发生运行期异常(RuntimeException),事务会进行回滚;如果发生一般的异常(Exception),事务不进行回滚。

    <property name="packagesToScan" value="edu.shao.springHibernate.po" /> 让Hibernate自动扫描指定的包下面注解的实体类,也就是User类。

    5、Dao接口及实现

    AbstractHibernateDao是一个抽象的Dao的父类,实现了一般的保存、删除、查询等方法。 具体的Dao类可以继承该类,并实现对某一实体特殊的访问方法。

    AbstractHibernateDao类中注入了SessionFactory ,通过方法“sessionFactory.getCurrentSession();”来获得一个session,用此session保存、更新、删除、查找实体。

    这里因为session.get()方法,都需要传入一个Class类型的参数,所以定义了entityClass字段,在具体Dao的构造方法中传入,下面会看到。

    import java.io.Serializable;
    import java.util.List;
    
    import org.hibernate.Query;
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    
    public abstract class AbstractHibernateDao <E extends Serializable>{
        private Class<E> entityClass;
        private SessionFactory sessionFactory;
         
        protected AbstractHibernateDao(Class<E> entityClass) {
            this.entityClass = entityClass;
        }
     
        public Session getCurrentSession() {
            return sessionFactory.getCurrentSession();
        }
     
        public E findById(Serializable id) {
            return (E) getCurrentSession().get(entityClass, id);
        }
     
        public void save(E e) {
            getCurrentSession().save(e);
        }
     
        public void delete(E e) {
            getCurrentSession().delete(e);
        }
     
        public List<E> query(String hql, Object[] args) {
            Query query=getCurrentSession().createQuery(hql);
            if(args!=null){
                for (int i = 0; i < args.length; i++) {
                    query.setParameter(i, args[i]);
                }
            }
            return query.list();
        }
    
        //setter getter
    }

    下面是具体的Dao的接口和实现。

    UserDaoImpl在构造参数中传入了User.class,用于初始化AbstractHibernateDao里的entityClass字段 。

    public interface IUserDao {
        public void save(User user);
        public List<User> query(String sql,Object[] args);
    }
    
    public class UserDaoImpl extends AbstractHibernateDao<User> implements IUserDao {
    
        public UserDaoImpl() {
            super(User.class);
        }
    
    }

    6、Service接口及实现

    public interface IUserService {
        void saveUser();
        void saveUserThrowException() throws Exception;
        void findUsers();
    }
    import java.util.List;
    
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import edu.shao.springHibernate.dao.IUserDao;
    import edu.shao.springHibernate.po.User;
    import edu.shao.springHibernate.service.IUserService;
    
    @Transactional
    public class UserService implements IUserService{
        private IUserDao userDao;
        
        public void saveUser() {
            User u1=new User();
            u1.setName("邵");
            u1.setAge(24);
            userDao.save(u1);
            
            //测试时,可以添加此异常,或去掉异常,测试Spring对事物的管理
    //        if(1+1>1){
    //            throw new RuntimeException("Runtime error...");//抛出运行时异常:RuntimeException
    //        }
            
            User u2=new User();
            u2.setName("陈");
            u2.setAge(20);
            userDao.save(u2);
        }
    
        @Transactional(rollbackFor={Exception.class})
        public void saveUserThrowException() throws Exception {
            User u1=new User();
            u1.setName("邵");
            u1.setAge(24);
            userDao.save(u1);
            
            if(1+1>1){
                throw new Exception("Runtime error...");//抛出一般的异常:Exception
            }
            
            User u2=new User();
            u2.setName("陈");
            u2.setAge(20);
            userDao.save(u2);
            
        }
        
        @Transactional(propagation=Propagation.REQUIRED,readOnly=true) 
        public void findUsers() {
            List<User> users=userDao.query("from User where name=?", new Object[]{"邵"});
            for (User user : users) {
                System.out.println(user);
            }
        }
        
        public IUserDao getUserDao() {
            return userDao;
        }
        public void setUserDao(IUserDao userDao) {
            this.userDao = userDao;
        }
    }

    如果没有事务,执行代码“sessionFactory.getCurrentSession(); ”,会抛出No Session found for current thread。

    getCurrentSession() 只有在一个事务之内才有意义,所以hibernate4要求最小事务级别必须是“Required”,如果是以下的级别,或者没有开启事务的话,无法得到当前的Session。

    上面代码中,propagation参数,至少要到REQUIRED,否则会抛出异常:No Session found for current thread

    对于运行时,这个可能不是很大的问题,因为在Service层一般都会开启事务,只要保证级别高于Required就可以了。

    更多关于Spring事务的介绍,情况上一篇随笔:三种数据库访问——Spring JDBC

    7、测试

    package edu.shao.springHibernate;
    
    import org.junit.BeforeClass;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import edu.shao.springHibernate.service.IUserService;
    
    public class SpringHibernateTest {
        private static ApplicationContext ctx = null;
    
        @BeforeClass 
        public static void onlyOnce() {
             ctx = new ClassPathXmlApplicationContext("db/applicationContext-hibernate.xml");
        }
    
        @Test
        public void testSave(){
            IUserService service=ctx.getBean("userService",IUserService.class);
            service.saveUser();
        }
        
    //    @Test
        public void testSaveThrowException() throws Exception{
            IUserService service=ctx.getBean("userService",IUserService.class);
            service.saveUserThrowException();
        }
        
    //    @Test
        public void testJDBCDaoQuery(){
            IUserService service=ctx.getBean("userService",IUserService.class);
            service.findUsers();
        }
    }

    测试结果,在这里就不说了。

    参考:http://kyfxbl.iteye.com/blog/1634355

  • 相关阅读:
    8-JS闭包、回调实例
    7-闭包、回调
    6-JS函数(二)
    5-JS函数
    4-JS对象
    3-WebPack
    2-Babel
    1-NPM
    25-React事件处理及条件渲染
    java初始化笔记
  • 原文地址:https://www.cnblogs.com/windlaughing/p/3289552.html
Copyright © 2011-2022 走看看