• Spring之JDBC


    jdbc.properties

    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/ssi?useUnicode=true&characterEncoding=UTF-8
    username=root
    password=123456
    initialSize=1
    maxActive=500
    maxIdle=2
    minIdle=1

    配置数据源

    <?xml version="1.0" encoding="UTF-8"?>
    <!--Spring主配置文件 引入模块 数据源配置-->
    <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:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
    
    
        <!--数据源-->
        <context:property-placeholder location="classpath:jdbc.properties"/>
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            <property name="driverClassName" value="${driverClassName}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
            <!--初始连接数-->
            <property name="initialSize" value="${initialSize}"/>
            <!--最大连接数-->
            <property name="maxActive" value="${maxActive}"/>
            <!--最大空闲数-->
            <property name="maxIdle" value="${maxIdle}"/>
            <!--最小空闲数-->
            <property name="minIdle" value="${minIdle}"/>
        </bean>
    
        <!--JDBC数据源事务管理器-->
        <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <!--使用注解配置事务-->
        <tx:annotation-driven transaction-manager="txManager"/>
        <!-- 所有使用Spring JDBC的类需要注入jdbcTemplate -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <!--引入测试模块 使用相对路径-->
        <import resource="org/zln/config/spring/test/spring-config-test.xml"/>
        <!--依赖注入-->
        <import resource="org/zln/config/spring/test2_ioc/spring-config-test2.xml"/>
        <!--面向切面-->
        <import resource="org/zln/config/spring/test3_aop/spring-config-test3.xml"/>
        <!--Spring继承JDBC-->
        <import resource="org/zln/config/spring/test4_spring_jdbc/spring-config-test4.xml"/>
    </beans>

    Dao

    package org.zln.module.test4_spring_jdbc.dao.impl;
    
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.core.RowMapper;
    import org.zln.module.test4_spring_jdbc.dao.PersonDao;
    import org.zln.module.test4_spring_jdbc.domain.Person;
    
    import javax.annotation.Resource;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Types;
    import java.util.List;
    
    /**
     * Created by coolkid on 2015/6/6 0006.
     */
    public class PersonDaoImpl implements PersonDao {
    
    
        @Resource
        private JdbcTemplate jdbcTemplate;
    
    
        @Override
        public void save(Person person) {
            String sql = " INSERT INTO person (name) VALUES (?) ";
            jdbcTemplate.update(sql, new Object[]{person.getName()}, new int[]{Types.VARCHAR});
        }
    
        @Override
        public void update(Person person) {
            String sql = " UPDATE person SET name = ? WHERE id = ? ";
            jdbcTemplate.update(sql,new Object[]{person.getName(),person.getId()},new int[]{Types.VARCHAR,Types.INTEGER});
        }
    
        @Override
        public Person getPerson(Integer id) {
            String sql = " SELECT id,name FROM person WHERE id = ? ";
            Person person = new Person();
            jdbcTemplate.queryForObject(sql, new Object[]{id}, new int[]{Types.INTEGER},new RowMapper() {
                @Override
                public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
                    person.setId(rs.getInt("id"));
                    person.setName(rs.getString("name"));
                    return person;
                }
            });
            return person;
        }
    
        @Override
        public List<Person> getPersonList() {
            String sql = " SELECT id,name FROM person ";
            List<Person> persons = jdbcTemplate.query(sql, new RowMapper() {
                @Override
                public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
                    Person person = new Person();
                    person.setId(rs.getInt("id"));
                    person.setName(rs.getString("name"));
                    return person;
                }
            });
            return persons;
        }
    
        @Override
        public void delete(Integer id) {
            String sql = " DELETE FROM person WHERE id = ? ";
            jdbcTemplate.update(sql,new Object[]{id},new int[]{Types.INTEGER});
        }
    }

    Service

    package org.zln.module.test4_spring_jdbc.service.impl;
    
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    import org.zln.module.test4_spring_jdbc.dao.PersonDao;
    import org.zln.module.test4_spring_jdbc.domain.Person;
    import org.zln.module.test4_spring_jdbc.service.PersonService;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    /**
     * Created by coolkid on 2015/6/6 0006.
     */
    /*打开事务注解后,默认同一个方法属于同一个事务。否则,每条SQL单独自己一个事务*/
    @Transactional(rollbackFor = Exception.class)
    public class PersonServiceImpl implements PersonService {
    
        @Resource(name = "personDaoJdbc")
        private PersonDao personDao;
    
        @Override
        public void save(Person person) {
            personDao.save(person);
        }
    
        @Override
        public void update(Person person) {
            personDao.update(person);
        }
    
        @Override
        @Transactional(propagation = Propagation.NOT_SUPPORTED)
        public Person getPerson(Integer id) {
            return personDao.getPerson(id);
        }
    
        @Override
        @Transactional(propagation = Propagation.NOT_SUPPORTED)
        public List<Person> getPersonList() {
            return personDao.getPersonList();
        }
    
        @Override
        public void delete(Integer id) {
            personDao.delete(id);
        }
    }

    配置

    <?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-2.5.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    
        <!--开启注解注入-->
        <context:annotation-config/>
    
        <bean id="personDaoJdbc" class="org.zln.module.test4_spring_jdbc.dao.impl.PersonDaoImpl"/>
        <bean id="personServiceJdbc" class="org.zln.module.test4_spring_jdbc.service.impl.PersonServiceImpl"/>
    </beans>

    事务传播属性

    在默认情况下,也就是对于我们交给Spring进行事务管理的Bean,只是在类上添加了一个 @Transactional 注解,那么每个方法都是单独的一个事务
    各自方法中的所有SQL都属于同一个事务。
    
    默认情况下
    如果方法在运行过程中发生了运行时异常(unchecked exception),也就是抛出了 RuntimeException ,方法中的更新会被取消。也就是这个事务被回滚
    如果方法在运行过程中发生了检查异常(checkded exception),也就是抛出了 Exception(这种方法我们要么继续往上层抛出,要么自己catch),那么方法中的更新不会回滚
    
    如果对于某个方法,即使发生的是检查例外,也需要进行事务的回滚,那么就需要添加注解 @Transactional(rollbackFor=Exception.class) ,这个注解说明,抛出Exception,就要回滚
    
    如果对于某个方法,发生运行期意外,但是不需要事务的回滚,那么需要添加注解 @Transactional(noRollbackFor=RuntimeException.class),这个注解说明,抛出RuntimeException,不需要回滚
    
    对于一些查询类的方法,不会改变数据,这个时候就没必要添加事务,可以添加传播属性取消事务的开启  @Transactional(propagation = Propagation.NOT_SUPPORTED) 
    
    一般我们把事务注解添加在 service(业务) 层,service层调用Dao层。Dao层是真正直接操作数据库的
    ----------------------------------------------------------------------------------------------------------------------------------
    
    事务常用注解
    <tx:annotation-driven transaction-manager="txManager"/> -- 在使用注解配置事务之前,首先要在Spring配置文件中开启处理这些注解的解析器
    
    @Transactional(rollbackFor=Exception.class),checked exception异常回滚(默认不回滚)
    @Transactional(noRollbackFor=RuntimeException.class),unckecked exception异常不回滚(默认回滚)
    @Transactional(propagation = Propagation.NOT_SUPPORTED) ,不把这个方法添加到事务管理中(默认某个类添加了@Transaction后,每个方法都会单独开启一个事务)
    
    事务传播属性
    REQUIRED(默认)    业务方法需要在一个事务中允许,如果方法运行时已经处于某个事务中了,就加入到该事务中,否则自己创建一个新的事务
    NOT_SUPPORTED    声明方法不需要事务,如果方法没有管理到一个事务,容器不会为其开启事务,如果方法在事务中被调用,该事务会被挂起。方法调用结束后便恢复执行
    REQUIRESNEW        声明业务方法总是为自己开启一个新事务,如果方法运行在一个事务中,那么原事务会被挂起,新事务创建;方法结束后,原事务恢复执行
    MANDATORY        声明方法执行运行在一个已经存在的事务中,方法自己不能发起事务,如果调用该方法的时候不存在事务,就会抛出异常
    SUPPORTS        如果方法已经在某个事务中执行了,则成为该事务的一部分;如果在事务外执行,则没有事务
    NEVER            声明该方法绝对不能在事务范围内执行;否则抛出异常
    NESTED            如果存在一个事务,则嵌套在事务中运行,如果没有活动事务,则按RESUIRED属性执行。它使用一个单独的事务,这个事务可以拥有多个回滚点,内部事务的回滚不对
                        外部事务造成影响。它只对DataSourceTransactionManager事务管理器有效
    
    其他设置
    @Transactional(readOnly = true)    设置只读属性,默认false,提高效率
    timeout            事务的超时时间,默认30秒
    isolate            数据库的隔离级别,依赖于底层的数据库,不是Spring实现的。设置不同的个例级别,就是在准确性与并发效率上做一个取舍。一般就是由数据库默认的隔离级别
                    大部分数据库的隔离级别是 Read Commited(会出现不可重复读和幻读)
                    MySQL的默认隔离级别是 Repeatable Read(出现幻读)
                    
                    脏读:一个事务读取另一个事务未提交的数据
                    不可重复读:同一个事务中的读取结果不一致,因为后续读取时有其他事务提交新的更新
                    幻读:一个事务读取另一个事务提交的insert

    数据并发可能导致的问题
        1 脏读
            A事务读取了B事务尚未提交的更改,并在此基础上进行操作,此时如果B事务回滚,那么A事务的操作根本是不被承认的
            (Oracle中不会发生脏读)
        2 不可重复读
            A事务读取了B事务已经提交的更改数据
            说明:A在同一个事务中进行了两次读取,期间B事务提交了修改,导致A事务在一个事务中两次相同的读取结果不一致
        3 幻读
            A事务读取了B事务提交的新增数据
            说明:幻读与不可重复读的区别,防止幻读加表级锁(Oracle使用多版本数据的方式实现),防止不可重复读,只需要加行级锁

        第一类丢失更新
            A事务撤消时,把已经提交的B事务的更新数据覆盖了
        第二类丢失更新
            A事务覆盖了B事务已经提交的数据,造成B事务的操作无效

    数据库锁机制
        按锁定对象划分
            表锁定
            行锁定(INSERT UPDATE DELETE SELECT FOR UPDATE 都隐含必要的行锁定)
        按锁定关系划分
            共享锁
            独占锁
    Oracle中的5种锁定
        行共享锁定:并不防止对数据库的更改操作,但是防止其它会话获取独占性数据锁定.允许进行多个并发的行共享和独占性锁定,还允许进行数据表的共享或者采取共享行毒战锁定
        行独占锁定
        表共享锁定
        表共享行锁定
        表独占

    事务隔离级别
        见图

    不同持久层技术的事务管理器实现类
        Hibernate2.x    org.springframework.orm.hibernate.HibernateTransactionManager
        Hibernate3.0    org.springframework.orm.hibernate3.HibernateTransactionManager
        JDBC/iBatis     org.springframework.jdbc.datasource.DataSourceTransactionManager

    事务同步管理器
        Spring将JDBC的Connection Hibernate的Session等数据库的连接或会话的对象统称为资源
        这些资源在同一时刻是不能多线程共享的
        为了让Dao Service类可能做到singleton,Spring提供了线程绑定资源获取工具
        Spring JDBC/iBatis  org.springframework.jdbc.datasource.DataSourceUtils
        Hibernate           org.springframework.orm.hibernate.SessionFactoryUtils
        Hibernate3.0        org.springframework.orm.hibernate3.SessionFactoryUtils

    事务传播行为
        PROPAGATION_REQUIRED    如果当前没有事务就新建一个事务,如果已经存在一个事务中,就加入到这个事务中(这是最常见的选择)
        PROPAGATION_SUPPORTS    支持当前事务,如果当前没有事务,就以非事务方式执行
        PROPAGATION_MANDATORY   使用当前事务,如果当前没有事务,就抛出异常
        PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,就把当前事务挂起
        PROPAGATION_NOT_SUPPORTED以非事务方式操作,如果当前存在事务,就把当前事务挂起
        PROPAGATION_NEVER       以非事务方式执行,如果当前存在事务,就抛出异常
        PROPAGATION_NESTED      如果当前存在事务,就嵌套在事务中运行,如果没有事务,执行与 PROPAGATION_REQUIRED 类似的操作
                                说:使用 PROPAGATION_NESTED 时,底层数据源必须基于JDBC3.0,并且实现者支持保存点事务机制

  • 相关阅读:
    docker安装kafka
    Prometheus警报
    MongoDB介绍
    SpringMvc中几个注解
    无DNS安装VCSA
    互联网本质
    什么是领导力
    58沈剑_一分钟专栏
    以数据库思维理解区块链
    区块链的4个实际应用
  • 原文地址:https://www.cnblogs.com/sherrykid/p/4573940.html
Copyright © 2020-2023  润新知