spring jdbc及事务管理

Spring提供了一个jdbc模板,它类似于dbutils工具。

快速入门

创建数据库

CREATE DATABASE springtest;

USE springtest;

CREATE TABLE t_user(
    
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(20),
    age INT,
    sex VARCHAR(20)
)

INSERT INTO t_user VALUES(NULL,'tom',20,'');
INSERT INTO t_user VALUES(NULL,'fox',30,'');
INSERT INTO t_user VALUES(NULL,'tony',40,'');    

SELECT * FROM t_user;

C3P0开源连接池配置

  

<!-- 配置连接池对象 -->
    <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="jdbc:mysql:///spring"></property>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

为了便于修改
引入外部属性文件,在src下的db.properties文件

jdbc.jdbcUrl=jdbc:mysql:///spring
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=root

在applicationContext.xml文件中引入

<context:property-placeholder location="classpath:db.properties"/>

所以C3P0连接池的引入可以改为

<!-- 配置c3p0连接池 -->
    <bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

在自己配置中需要从properties文件中引入的信息可以使用${name}方式来获取

 

JdbcTemplate CRUD

执行insert update delete操作

只需要使用JdbcTemplate的update方法就可以执行insert update delete操作

@Autowired
    @Qualifier("jdbcTemplate")
    private JdbcTemplate template;

    @Test
    public void test1() {
        // update执行update操作
        template.update("update t_user set name = ? where id = ?", "猥琐逼", 1);
    }

    @Test
    public void test2() {
        // update执行update操作
        template.update("insert into t_user values(null,?,?,?)", "龟哥", 25, "男");
    }

    @Test
    public void test3() {
        // update执行delete操作
        template.update("delete from t_user where id = ?", 4);
    }

执行select操作

简单数据返回

/*
     * 下面测试查询语句返回简单的数据类型
     */
    @Test
    public void test4() {
        String name = template.queryForObject("select name from t_user where id = 1", String.class);
        System.out.println(name);
    }

    @Test
    public void test5() {
        Integer count = template.queryForObject("select count(*) from t_user", Integer.class);
        System.out.println(count);
    }

复杂数据返回

/*
     * 下面测试复杂返回类型
     */
    @Test
    public void test6() {
        // 如果返回的是一个对象,可以使用queryForObject方法
        User user = template.queryForObject("select * from t_user where id = ?", new RowMapper<User>() {

            @Override
            public User mapRow(ResultSet rs, int arg1) throws SQLException {
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setName(rs.getString("name"));
                user.setAge(rs.getInt("age"));
                user.setSex(rs.getString("sex"));
                return user;
            }
        },1);
        System.out.println(user);
    }
    
    @Test
    public void test7() {
        // 如果返回的是一个集合,使用query方法
        List<User> list = template.query("select * from t_user", new RowMapper<User>() {

            @Override
            public User mapRow(ResultSet rs, int arg1) throws SQLException {
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setName(rs.getString("name"));
                user.setAge(rs.getInt("age"));
                user.setSex(rs.getString("sex"));
                return user;
            }
        });
        System.out.println(list);
    }


注意:如果只返回一个domain对象,可以使用queryForObject方法,如果返回的是List<?>对象,
可以使用query方法,但是都需要使用RowMapper来对ResultSet进行处理。

RowMapper它有一个实现类叫BeanPropertyRowMapper
如果使用BeanPropertyRowmapper,实体类必须提供一个无参数的public构造方法,类中的bean属性名称与表中的列要对应

@Test
    public void test8() {
        // 使用rowMapper的实现类BeanPropertyRowMapper
        /*List<User> list = template.query("select * from t_user", new BeanPropertyRowMapper<User>(User.class));
        System.out.println(list);*/
        
        List<User> user = template.query("select * from t_user where id = 1", new BeanPropertyRowMapper<User>(User.class));
        System.out.println(user);
    }

spring的事务管理

下面通过一个转账案例来了解spri

创建数据库表

CREATE TABLE account(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(20),
    money DOUBLE
)

INSERT INTO account VALUES(NULL,'tom',1000);
INSERT INTO account VALUES(NULL,'fox',1000);

 

创建service于dao

@Service("accountService")
public class AccountServiceImpl implements AccountService{
    @Autowired
    @Qualifier("accountDao")
    private AccountDaoImpl ad;
    @Override
    public void account(String outname, String inname, double money) {
        ad.accountIn(inname,money);
        //int a = 10 /0;
        ad.accountOut(outname,money);
    }
}
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao{
    @Autowired
    @Qualifier("jdbcTemplate")
    private JdbcTemplate temple;
    @Override
    public void accountIn(String inname, double money) {
        temple.update("update account set money = money + ? where name = ?",money,inname);
    }

    @Override
    public void accountOut(String outname, double money) {
        temple.update("update account set money = money - ? where name = ?",money,outname);
    }
}

不要忘了在applicationContext中配置开启注解

<!-- 开启注解配置 -->
    <context:component-scan base-package="com.learn"></context:component-scan>

如果打开//int a = 10 /0;

就一定会排出异常
这时我们就需要事务控制,让事务具有一致性

 

Spring事务管理主要提供了三个接口来完成

1. org.springframework.transaction.PlatformTransactionManager
这是一个事务管理器,可以来选择相关的平台(jdbc hibernate jpa…)
2. TransactionDefinition
它定义事务的一些相关信息 例如 隔离 传播 超时 只读
3. TransactionStatus
它主要描述事务具体的运行状态

 

PlatformTransactionManager:
  平台事务管理器,在不同的持久化层解决技术它的事务代码不一样。
  DataSourceTransactionManager 主要针对于JdbcTemplate开发 MyBatis开发
  HibernateTransactionManasger主要针对于Hibernate开发
  JpaTransactionManager 主要针对于JPA开发。

TransactionDefinition:
  它描述的是事务的定义信息。
  在TransactionDefinition中定义了大量的常量
  事务的四个特性 ACID 原子性 一致性 隔离性 持久性。
  不考虑事务隔离性会出现:脏读,不可重复读 虚读。
  ISOLATION_DEFUALT 它使用后端数据库的默认隔离级别(spring中选项)
  ISOLATION_READ_UNCOMMITTED 不能解决问题,会发生脏读 不可重复读 虚读
  ISOLATION_READ_COMMITTED 可以解决脏读 会产生不可重复读与虚读。
  ISOLATION_REPEATABLE_READ 可以解决脏读,不可重复读 解决不了虚读
  ISOLATION_SERIALIZABLE 串行化,可以解决所有问题
  对于不现的数据库,它的底层默认事务隔离级别不一样。
  Oracle数据库它默认的是read_committed
  Mysql数据库它默认的是repeatable_read.

  下面再介绍一下它的传播特性:

    它解决的是两个被事务管理的方法互相调用问题。它与数据库没关系,是程序内部维护的问题。

  最常用的三种:  

    PROPAGATION_REQUIRED 默认值 两个操作处于同一个事务,如果之前没有事务,新建一个事务
    PROPAGATION_REQUIRES_NEW,两个操作处于不同的事务
    PROPAGATION_NESTED,它是一种嵌套事务,它是使用SavePoint来实现的。

事务回滚时可以回滚到指定的savepoint,注意:它只对DataSourceTransactionManager有作用

 

TransactionStatus:

它定义了事务状态信息,在事务运行过程中,得到某个时间点的状态

事务管理方式

  1. 编码方案 不建议使用,它具有侵入性。在原有的业务代码基础上去添加事务管理代码
  2. 声明式事务控制,基于AOP对目标进行代理,添加around环绕通知。
这种方案,它不具有侵入性,不需要修改原来的业务代码

 

基于xml配置声明式事务管理方案

<!-- 配置开启事务 -->
    <!-- 配置事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="c3p0DataSource"></property>
    </bean>
    <!-- 配置通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- name:必须的,对哪些方法进行事务控制 isolation 可选 设置事务隔离级别 默认是DEFAULT propagation:可选 
                设置事务传播 默认值 REQUIRED timeout 可选 超时时间 默认值-1 read-only 可选 默认值是false 如果不是只读,它可以对insert 
                update delete操作,如果是只读不可以。 rollback-for 可选 可以设置一个异常,如果产生这个异常,触发事务回滚 no-rolback-for 
                可选 可以设置一个异常,如果产生这个异常,不会触发事务回滚 -->
            <tx:method name="account" />

        </tx:attributes>
    </tx:advice>
    <!-- 配置切面 -->
    <aop:config>
        <aop:pointcut
            expression="execution(* com.learn.service.AccountService.account(..))"
            id="txPointcut" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
    </aop:config>

基于annotation声明式事务管理方案

<!-- 开启注解配置事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

在需要开启的类或方法上声明

@Transactional

 

posted @ 2017-07-09 15:54  司机刹一脚  阅读(2759)  评论(0编辑  收藏  举报