第四章 Spring与JDBC的整合
这里选择的是mysql数据库。
4.1引入aop、tx的命名空间
为了事务配置的需要,我们引入aop、tx的命名空间
<?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:p="http://www.springframework.org/schema/p" 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/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
4.2配置数据源
<!-- 配置数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- jdbc连接的4个必须参数 --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/> <property name="username" value="root"/> <property name="password" value="test"/> <!-- 连接池启动初始值 --> <property name="initialSize" value="5"/> <!-- 最大空闲值 --> <property name="maxIdle" value="20"/> <!-- 最小空闲值 --> <property name="minIdle" value="5"/> <!-- 最大连接值 --> <property name="maxActive" value="500"/> </bean>
4.3配置事务
4.3.1 基于Schema的方式
<!-- 指定事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 设置事务增强 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="get*" read-only="true"/> <tx:method name="add*" rollback-for="Exception"/> </tx:attributes> </tx:advice> <!-- 作用Shcema的方式配置事务,这里是把事务设置到了service层--> <aop:config> <aop:pointcut id="servicePointcut" expression="execution(* cn.framelife.spring.jdbc.*ServiceImpl.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut"/> </aop:config>
4.3.2基于注解的方式
基本配置:
<!-- 配置数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- jdbc连接的4个必须参数 --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/> <property name="username" value="root"/> <property name="password" value="test"/> <!-- 连接池启动初始值 --> <property name="initialSize" value="5"/> <!-- 最大空闲值 --> <property name="maxIdle" value="20"/> <!-- 最小空闲值 --> <property name="minIdle" value="5"/> <!-- 最大连接值 --> <property name="maxActive" value="500"/> </bean> <!-- 指定事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 打开tx事务注解管理功能 --> <tx:annotation-driven transaction-manager="txManager"/>
@Transactional的使用
放在需要事务的类的头上。如:我们想把事务集中在业务层管理,那就在service层的各个类上标注上@Transactional注解。
@Transactional @Repository public class UserDaoImpl implements UserDao { }
4.3.3事务的传播行为
Spring给我们提供了7种类型的事务传播行为:
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
我们可以通过事务的propagation属性来进行配置。
4.3.4事务的隔离级别
事务的隔离级别是由数据库本身提供的,一般不需要我们去改变什么东西。
-
事务并发会产生的问题
-
脏读 如:事务A读到事务B未提交的数据
-
不可重复读如:多次读取同一个事务得到的结果不同
-
幻读如:事务A读到事务B已提交的数据
-
隔离级别
隔离级别 |
解释 |
允许并发的问题 |
相应数据库 |
READUNCOMMITED |
可读取未提交的数据 |
ABC |
|
READCOMMITED |
读已提交事务 |
BC |
SQLServer默认 |
REPEATABLEREAD |
可重复读 |
C |
MySQL默认 |
SERIALIZABLE |
序列化 |
|
|
4.4JDBCTemplate的使用
public class UserDaoImpl implements UserDao { private JdbcTemplate jdbcTemplate; /** * 通过dataSource的setter方法,在运行时注入一个dataSouce对象,然后根据这个对象创建一个JdbcTemplate对象 * @param dataSource */ public void setDataSource(DataSource dataSource){ this.jdbcTemplate = new JdbcTemplate(dataSource); } /** * 增加、修改、删除都是使用的JdbcTemplate的update方法。 * update方法中第一个参数是sql语句,未知的值用占位符?代替 * 第二个参数是一个Object数组。数组里面的各项是上面的占位符的值 * 第三个参数是一个int数组。数组的各项是第二个参数中的值在数据库中的类型 */ public void save(User user) { jdbcTemplate.update("insert into user(username,password) values(?,?)", new Object[]{user.getUsername(),user.getPassword()}, new int[]{java.sql.Types.VARCHAR,java.sql.Types.VARCHAR} ); } public void update(User user) { jdbcTemplate.update("update user set username=?,password=? where id=?", new Object[]{user.getUsername(),user.getPassword(),user.getId()}, new int[]{java.sql.Types.VARCHAR,java.sql.Types.VARCHAR,java.sql.Types.INTEGER} ); } public void delete(Integer id) { jdbcTemplate.update("delete from user where id = ?", new Object[]{id}, new int[]{java.sql.Types.INTEGER} ); } /** * 根据id获取单个数据是通过queryForObject方法 * 这个方法前面三个参数都与update方法一样 * 第四个参数是一个org.springframework.jdbc.core.RowMapper接口的对象 * 实现RowMapper接口,必须实现里面的mapRow(ResultSet rs, int rowNum)方法 * 这个方法是通过ResultSet把一条记录放到一个实体类对象中,并返回这个实体类对象 */ public User getUserById(Integer id) { User user = jdbcTemplate.queryForObject("select * from user where id=?", new Object[]{id}, new int[]{java.sql.Types.INTEGER}, new RowMapper<User>() { public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); user.setPassword(rs.getString("password")); return user; } }); return user; } /** * 通过一条没有占位符的select语句来查询多条记录,并返回一个List集合 * query方法里面的两个参数 * 第一个是select语句 * 第二个是RowMapper接口的对象 */ public List<User> getUsersBySql(String sql) { return jdbcTemplate.query(sql, new RowMapper<User>() { public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); user.setPassword(rs.getString("password")); return user; } }); } }
4.5完整的配置
4.5.1基于Schema
<?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:p="http://www.springframework.org/schema/p" 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/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <!-- 配置数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- jdbc连接的4个必须参数 --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/> <property name="username" value="root"/> <property name="password" value="test"/> <!-- 连接池启动初始值 --> <property name="initialSize" value="5"/> <!-- 最大空闲值 --> <property name="maxIdle" value="20"/> <!-- 最小空闲值 --> <property name="minIdle" value="5"/> <!-- 最大连接值 --> <property name="maxActive" value="500"/> </bean> <!-- 指定事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!— 设置事务增强 read-only 表示对应的事务应该被最优化为只读事务。 rollback-for="Exception" 是指出现异常的时候回滚事务 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="get*" read-only="true"/> <tx:method name="add*" rollback-for="Exception"/> <tx:method name="update*"/> </tx:attributes> </tx:advice> <!-- 作用Shcema的方式配置事务 --> <aop:config> <aop:pointcut id="servicePointcut" expression="execution(* cn.framelife.spring.jdbc.*ServiceImpl.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut"/> </aop:config> <!-- 把相关的Bean交由Spring管理 --> <bean id="userDao" class="cn.framelife.spring.jdbc.UserDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="userService" class="cn.framelife.spring.jdbc.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> </beans>
<?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:p="http://www.springframework.org/schema/p" 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/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <context:component-scan base-package="cn.framelife.spring"></context:component-scan> <!-- 配置数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- jdbc连接的4个必须参数 --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/> <property name="username" value="root"/> <property name="password" value="test"/> <!-- 连接池启动初始值 --> <property name="initialSize" value="5"/> <!-- 最大空闲值 --> <property name="maxIdle" value="20"/> <!-- 最小空闲值 --> <property name="minIdle" value="5"/> <!-- 最大连接值 --> <property name="maxActive" value="500"/> </bean> <!-- 指定事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 打开tx事务注解管理功能 --> <tx:annotation-driven transaction-manager="txManager"/> </beans>