# Spring-JDBC与Spring 事务
# 使用c3p0链接池配置信息
代码语言:javascript复制jdbc.user=root
jdbc.password=xxxxxx
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/Xxxx?useSSL=false&serverTimezone=UTC
jdbc.initPoolSize=5
jdbc.maxPoolSize=20
# spring配置文件
代码语言:javascript复制<?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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--导入资源配置文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
<!--配置spring的JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
# spring-jdbc测试
代码语言:javascript复制package top.finen.spring.jdbc;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class JDBCTest {
private ApplicationContext ctx = null;
private JdbcTemplate jdbcTemplate;
{
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");
}
/**
* 批量更新
* 最后一个参数是Object[]的List类型:因为修改一条记录需要一个Object的数组,那么多条就需要多个Object数组
*/
@Test
public void testBatchUpdate() {
String sql = "INSERT INTO employees(last_name, email, dept_id) VALUES(?, ?, ?)";
List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[]{"AA", "88888@qq.com", 1});
batchArgs.add(new Object[]{"BB", "88888@qq.com", 1});
batchArgs.add(new Object[]{"CC", "88888@qq.com", 3});
batchArgs.add(new Object[]{"VF", "88888@qq.com", 1});
batchArgs.add(new Object[]{"FFF", "88888@qq.com", 1});
batchArgs.add(new Object[]{"AGGA", "88888@qq.com", 1});
batchArgs.add(new Object[]{"WW", "88888@qq.com", 1});
jdbcTemplate.batchUpdate(sql, batchArgs);
}
@Test
public void testUpdate() {
String sql = "UPDATE employees SET last_name = ? WHERE id = ?";
jdbcTemplate.update(sql, "Tom", 3);
}
@Test
public void testDataSource() throws SQLException {
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource.getConnection());
}
/**
* 从数据库中获取一条记录,实际得到对应的一个对象
* 注意:不是调用queryForObject(String sql, Class<Employee> requireType, Object... args)方法,
* 而需要调用queryForObject(String sql, RowMapper<T> rowMapper, Object... args)
* 1. 其中的RowMapper指定如何如映射结果集的行,常用的实现类为BeanPropertyRowMapper
* 2.使用SQL中的列的别名完成列名和类的属性名的映射,例如:last_name lastName
* 3.不支持级联属性,JdbcTemplate到底是一个Jdbc的小工具,而不是ORM框架
*/
@Test
public void testQueryForObject() {
String sql = "SELECT id, last_name lastName, email FROM employees WHERE id = ?";
RowMapper<Employee> employeeRowMapper = new BeanPropertyRowMapper<Employee>(Employee.class);
Employee employee = jdbcTemplate.queryForObject(sql, employeeRowMapper, 1);
System.out.println(employee);
}
/**
* 查到实体类的集合
* 注意调用不是的queryForList
*/
@Test
public void testQueryForList() {
String sql = "SELECT id, last_name lastName, email FROM employees WHERE id > ?";
RowMapper<Employee> employeeRowMapper = new BeanPropertyRowMapper<Employee>(Employee.class);
List<Employee> employees = jdbcTemplate.query(sql, employeeRowMapper, 3);
System.out.println(employees);
}
/**
* 获取单列的值或者做统计查询
*
*/
@Test
public void testQueryForObject2() {
String sql = "SELECT count(id) FROM employees";
long count = jdbcTemplate.queryForObject(sql, Long.class);
System.out.println(count);
}
}
# Spring使用具名参数
代码语言:javascript复制<!--配置namedParameterJdbcTemplate,该对象可以使用具名参数,其没有无参数的构造器指定参数-->
<bean id="namedParameterJdbcTemplate"
class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
代码语言:javascript复制@Test
public void testNamedParameterJdbcTemplate() {
String sql = "INSERT INTO employees(last_name, email, dept_id) VALUES(:ln, :email, :deptid)";
Map<String, Object> paraMap = new HashMap<>();
paraMap.put("ln", "FF");
paraMap.put("email", "sss@888.com");
paraMap.put("deptid", 2);
namedParameterJdbcTemplate.update(sql, paraMap);
}
/**
* 使用具名参数时,可以update(String sql, SqlParameterSource paramSource)方法进行更新操作
* 1.SQL语句的参数与类的属性一致
* 2.使用SqlParameterSource的BeanPropertySqlParameterSource实现类作为参数
*/
@Test
public void testNamedParameterJdbcTemplate2() {
String sql = "INSERT INTO employees(last_name, email, dept_id) VALUES(:lastName, :email, :deptId)";
Employee employee = new Employee();
employee.setLastName("sssss");
employee.setEmail("@@@ssss.com");
employee.setDeptId(1);
SqlParameterSource sqlParameterSource = new BeanPropertySqlParameterSource(employee);
namedParameterJdbcTemplate.update(sql, sqlParameterSource);
}
@Test
public void testEmployeeDao() {
System.out.println(employeeDao.get(1));
}
# Spring的事物管理
代码语言:javascript复制<!--配置事物管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--启用事物注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
代码语言:javascript复制@Transactional
@Override
public void purchase(String username, String isbn) {
//1.获取书的单价
int price = bookShopDao.findBookPriceByIsbn(isbn);
//2.更新书的库存
bookShopDao.updateBookStock(isbn);
//3.更新用户余额
bookShopDao.updateUserAccount(username, price);
}
# Spring 事物传播属性、事物隔离级别、回滚
当事务方法被另一个事务方法调用时,必须制定事务应该如何传播。
事务的传播行为可以有传播属性指定,Spring订了7种类型的传播行为。
REQUIRED 如果有事务在运行,当前的方法就在这个内运行,否则,就启动另一个事务,并在自己的事务内运行。
REQUIRED_NEW 当前的方法必须启动新事务,并在他自己的事务内运行,如果有事务在运行,应该将它挂起。
SUPPORTS 如果有事务在运行,当前的方法就在这个事物内运行,否则它可以不运行在事务中。
NOT_SUPPORTS 当前的方法不应该运行在事物中,如果有运行的事务,将它挂起。
MANDATORY 当前方法必须运行在事务内部,如果没有正在运行的事务。就跑出异常。
NESTED 如果有事务在运行,当前的方法就应该在这个事务的嵌套事物内运行,否则,就启动一个新的事务。并在他自己的事务内运行。
代码语言:javascript复制/**
* 添加事物注解
* 使用propagation指定事务的传播行为,即当前事务方法被另一个事物调用时,如何使用事务
* 默认取值为REQUIRED,即使用调用方法的事务
* 使用isolation指定事物的隔离级别,最常用取值为READ_COMMITTED
* 默认情况下Spring的声明式事物对所有的运行异常进行回滚。也可以通过对应的属性进行设置。通常情况下去默认值即可。
* 使用readOnly指定是否只读,表示这个事物只读取数据但不更新数据,这样可以帮助数据库引擎优化事物,若真的是一个只读取数据的方法,应设置readOnly为true
* 使用timeout指定强制回滚之前事务可以占用的时间
* @param username
* @param isbns
*/
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 1000,
readOnly = false,
noRollbackFor = {UserAccountException.class})
@Override
public void checkout(String username, List<String> isbns) {
for (String isbn: isbns) {
bookShopService.purchase(username, isbn);
}
}
# 基于xml的配置
代码语言:javascript复制<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<context:component-scan base-package="top.finen.spring"></context:component-scan>
<!-- 配置 C3P0 数据源 -->
<bean id="dataSource1"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
<!-- 配置 Spirng 的 JdbcTemplate -->
<bean id="jdbcTemplate1"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource1"></property>
</bean>
<!-- 配置 bean -->
<bean id="bookShopDao1" class="top.finen.spring.tx.xml.BookShopDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate1"></property>
</bean>
<bean id="bookShopService1" class="top.finen.spring.tx.xml.BookShopServiceImpl">
<property name="bookShopDao" ref="bookShopDao1"></property>
</bean>
<bean id="cashier1" class="top.finen.spring.tx.xml.CashierImpl">
<property name="bookShopService" ref="bookShopService1"></property>
</bean>
<!--配置事务管理器-->
<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource1"></property>
</bean>
<!--事务属性-->
<tx:advice id="txAdvice" transaction-manager="transactionManager2">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入点,以及把事务切入点和事务属性关联-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* top.finen.spring.tx.xml.BookShopService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"></aop:advisor>
</aop:config>