三种数据库访问——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();
    }
}

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

三种数据库访问——Spring3.2 + Hibernate4.2

时间: 2024-08-05 15:13:06

三种数据库访问——Spring3.2 + Hibernate4.2的相关文章

qlserver、Mysql、Oracle三种数据库的优缺点总结

这篇文章主要介绍了sqlserver.Mysql.Oracle三种数据库的优缺点总结,需要的朋友可以参考下 一.sqlserver优点:易用性.适合分布式组织的可伸缩性.用于决策支持的数据仓库功能.与许多其他服务器软件紧密关联的集成性.良好的性价比等:为数据管理与分析带来了灵活性,允许单位在快速变化的环境中从容响应,从而获得竞争优势.从数据管理和分析角度看,将原始数据转化为商业智能和充分利用Web带来的机会非常重要.作为一个完备的数据库和数据分析包,SQLServer为快速开发新一代企业级商业应

功能齐全、效率一流的免费开源数据库导入导出工具(c#开发,支持SQL server、SQLite、ACCESS三种数据库),每月借此处理数据5G以上

软件名:DataPie 功能:支持SQL server.SQLite.ACCESS数据库的导入.导出.存储过程调用,支持EXCEL2007.EXCEL2003.ACCESS2007. CSV文件导入数据库,支持EXCEL.CSV.ZIP.ACCESS文件方式导出,支持数据拆分导出及自定义SQL查询与导出. 开发背景:作者从事财务管理工作,主要是出具集团的内部财务报表,随着公司精细化管理的需求,管理报表的数据量急速增长, 依赖EXCEL加工处理数据已经变得极为困难,因此团队全面转向关系数据库进行数

三种数据库日期转字符串对照sql server、oracle、mysql(V4.11)

三种数据库日期转换对照: http://blog.csdn.net/zljjava/article/details/17552741 SQL类型转换函数:cast(type1 as type2) 数据库中的日期类型 SQL SERVER DATE 1970-01-01 DATETIME 1970-01-01 00:00:00 Oracle DATE 2015-08-07 17:34:37 TIMESTAMP 1970-01-01 00:00:00.000000 是DATE的扩展类型,多了小数秒

MySQL、SQLServer2000(及SQLServer2005)和ORCALE三种数据库实现分页查询的方法

在这里主要讲解一下MySQL.SQLServer2000(及SQLServer2005)和ORCALE三种数据库实现分页查询的方法. 可能会有人说这些网上都有,但我的主要目的是把这些知识通过我实际的应用总结归纳一下,以方便大家查询使用. 下面就分别给大家介绍.讲解一下三种数据库实现分页查询的方法. 一. MySQL 数据库分页查询 MySQL数据库实现分页比较简单,提供了LIMIT函数.一般只需要直接写到sql语句后面就行了. LIMIT子句可以用来限制由SELECT语句返回过来的数据数量,它有

基础知识回顾第三篇数据库访问

前言:本篇主要针对数据库的操作,在这里不适用hibernate或者mybatis,用最原始的JDBC进行讲解,通过了解这些原理以后更容易理解和学习hibernate或mybatis. 1:jdbc的简单操作 首先需要下载jdbc驱动的jar包 如果不想下载在C:\Program Files (x86)\MySQL\Connector J 5.1.26位置也可以找到 如果是maven项目的话需要在pom上引入下面的依赖包 1 <dependency> 2 <groupId>mysql

sqlserver OpenRowSet 对应的三种数据库驱动

在使用sqlserver数据库的OpenRowSet函数时,会遇到三种驱动方式: 1. MSDASQL驱动SELECT TOP 10 * FROM OPENROWSET('MSDASQL', 'DRIVER={SQL Server};SERVER=xxx;UID=xx;PWD=xxx;', 'select * from AdventureWorks.HumanResources.Employee') 2. SQLNCLI驱动SELECT TOP 10 * FROM OPENROWSET('SQL

oracle,mysql,SqlServer三种数据库的分页查询总结

MySql: MySQL数据库实现分页比较简单,提供了 LIMIT函数.一般只需要直接写到sql语句后面就行了. LIMIT子 句可以用来限制由SELECT语句返回过来的数据数量,它有一个或两个参数,如果给出两个参数, 第一个参数指定返回的第一行在所有数据中的位置,从0开始(注意不是1),第二个参数指定最多返回行数.例如: select * from table    LIMIT 10;    #返回前10行 select * from table    LIMIT 0,10; #返回前10行

三种数据库总结

SqlServer 数据库 一.数据库的创建在开发中的步骤 1.1收集信息 1.2概念设计(E-R图) 1.3详细设计(数据库模型图) 1.4审核数据库(三大范式) 2.1 E-R 矩形:实体 椭圆:属性 菱形:关系 3.1 映射关系 一对一,一对多,多对一,多对多 4.1 三大范式 第一范式:每一列都要保证原子性,分的不能再分了 第二范式:在满足1NF基础上,每一张表只能表述一件事(有主键) 第三范式:在满足2NF基础上 ,确保除主键之外的列都必须和主键有直接的关系,而不是间接地关系 二.建库

EF三种数据库操作模型比较

https://blog.csdn.net/xiongmeiqin/article/details/80196089 EF 中 Code First 的数据迁移以及创建视图 写在前面: EF 中 Code First 的数据迁移网上有很多资料,我这份并没什么特别.Code First 创建视图网上也有很多资料,但好像很麻烦,而且亲测好像是无效的方法(可能是我太笨,没搞成功),我摸索出了一种简单有效的方法,这里分享给大家. EF是Entity Framework(实体框架)的简写,是微软出品的用来