zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    HTTPS证书申请相关笔记
    180508
    如何通过 AAR 形式集成 leakcanary-android 服务
    Mysql命令大全
    Python3.x和Python2.x的区别 (转)
    Python学习笔记(二)
    for循环处理列表的显示
    Python学习笔记
    python环境搭建
    Linux下JDK环境的配置
  • 原文地址:https://www.cnblogs.com/learnjfm/p/7141775.html
Copyright © 2011-2022 走看看