Table of Contents
3.1:更新sql:jdbcTemplate.update(sql, 1000, 5);
3.2:批量插入:jdbcTemplate.batchUpdate(sql, batchArgs);
3.3:查询单个对象或者单个字段值:jdbcTemplate.queryForObject();
3.4:查询结果为对象的集合:jdbcTemplate.query
3.5:查询返回list:jdbcTemplate.queryForList()
3.7:也可以使用namedParameterJdbcTemplate直接传入类对象
4.3:触发事务回滚异常---noRollbackFor和rollbackFor
4.4:事务隔离级别---数据库事务一个读,一个写就会发生问题;
四:JdbcTemplate
1:Spring配置c3p0的数据库连接池
1.1:导包:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.wkl</groupId> <artifactId>spring03JDBC</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.6.RELEASE</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> </dependencies> </project>
1.2:配置c3p0数据库连接池
<?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:jdbcproperties.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="jdbcUrl" value="${jdbc.url}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> </bean> </beans>
1.3:设置数据库连接池参数
jdbc.user=root jdbc.password=123456 jdbc.url=jdbc:mysql://127.0.0.1:3306/jdbc_template?serverTimezone=Hongkong jdbc.driverClass=com.mysql.cj.jdbc.Driver
1.4:测试
public class TestAop {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void test01(){
// UserInter bean = ioc.getBean(UserInter.class);
// bean.add();
// System.out.println(""+bean.getClass());
UserImpl userImpl = (UserImpl) ioc.getBean("userImpl");
userImpl.add();
System.out.println(""+userImpl.getClass());
}
}
2:Spring使用JdbcTemplate
2.1:导包:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.wkl</groupId> <artifactId>spring03JDBC</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.6.RELEASE</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <!--导入jdbctemplate包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.6.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> </dependencies> </project>
2.2:配置JdbcTemplate
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean>
2.3:测试
@Test
public void test02() throws SQLException {
JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);
System.out.println(""+bean);
}
3:JdbcTemplate的操作
3.1:更新sql:jdbcTemplate.update(sql, 1000, 5);
@Test
public void test02() throws SQLException {
String sql = "UPDATE employee set salary = ? where emp_id = ?";
int update = jdbcTemplate.update(sql, 1000, 5);
System.out.println(""+update);
}
3.2:批量插入:jdbcTemplate.batchUpdate(sql, batchArgs);
@Test
public void test03(){
String sql ="INSERT INTO employee(emp_name,salary) VALUES(?,?)";
//List<Object[]>
//List的长度就是sql语句要执行的次数
//Object[]:每次执行要用的参数
List<Object[]> batchArgs = new ArrayList<Object[]>();
batchArgs.add(new Object[]{"张三",1998.98});
batchArgs.add(new Object[]{"李四",2998.98});
batchArgs.add(new Object[]{"王五",3998.98});
batchArgs.add(new Object[]{"赵六",4998.98});
int[] is = jdbcTemplate.batchUpdate(sql, batchArgs);
for (int i : is) {
System.out.println(i);
}
}
3.3:查询单个对象或者单个字段值:jdbcTemplate.queryForObject();
@Test
public void test04(){
String sql = "SELECT emp_id empId,emp_name empName,salary FROM employee WHERE emp_id=?";
//RowMapper:每一行记录和javaBean的属性如何映射
Employee employee = null;
try {
Double object = jdbcTemplate.queryForObject(sql, Double.class);
employee = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Employee.class), 50);
} catch (DataAccessException e) {
}
System.out.println(employee);
}
3.4:查询结果为对象的集合:jdbcTemplate.query
@Test
public void test05(){
String sql = "SELECT emp_id empId,emp_name empName,salary FROM employee WHERE salary>?";
//封装List;集合里面元素的类型
List<Employee> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Employee.class), 4000);
for (Employee employee : list) {
System.out.println(employee);
}
}
3.5:查询返回list<map>:jdbcTemplate.queryForList()
@Test
public void test03() throws SQLException {
String sql = "select * from employee";
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
for (Map<String, Object> map : maps) {
System.out.println(""+map);
}
}
3.6:使用具名参数的的jdbcTemplate
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean>
@Test
public void test07(){
String sql = "INSERT INTO employee(emp_name,salary) VALUES(:empName,:salary)";
//Map
Map<String, Object> paramMap = new HashMap<>();
//将所有具名参数的值都放在map中;
paramMap.put("empName", "田七");
paramMap.put("salary", 9887.98);
int update = namedJdbcTemplate.update(sql, paramMap);
System.out.println(update);
}
3.7:也可以使用namedParameterJdbcTemplate直接传入类对象
@Test
public void test08(){
String sql = "INSERT INTO employee(emp_name,salary) VALUES(:empName,:salary)";
Employee employee = new Employee();
employee.setEmpName("哈哈");
employee.setSalary(998.98);
//
int i = namedJdbcTemplate.update(sql, new BeanPropertySqlParameterSource(employee));
System.out.println(i);
}
五:声明式事务
1:事务简介
●事务就是一组由于逻辑上紧密关联而合并成一个整体(工作单元)的多个数据库操作,这些操作要么都执行,要么都不执行。
●事务的四个关键属性(ACID)
○原子性(atomicity):“原子”的本意是“不可再分”,事务的原子性表现为一个事务中涉及到的多个操作在逻辑上缺一不可。事务的原子性要求事务中的所有操作要么都执行,要么都不执行。
○一致性(consistency):“一致”指的是数据的一致,具体是指:所有数据都处于满足业务规则的一致性状态。一致性原则要求:一个事务中不管涉及到多少个操作,都必须保证事务执行之前数据是正确的,事务执行之后数据仍然是正确的。如果一个事务在执行的过程中,其中某一个或某几个操作失败了,则必须将其他所有操作撤销,将数据恢复到事务执行之前的状态,这就是回滚。
○隔离性(isolation):在应用程序实际运行过程中,事务往往是并发执行的,所以很有可能有许多事务同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。隔离性原则要求多个事务在并发执行过程中不会互相干扰。
○持久性(durability):持久性原则要求事务执行完成后,对数据的修改永久的保存下来,不会因各种系统错误或其他意外情况而受到影响。通常情况下,事务对数据的修改应该被写入到持久化存储器中。
2:以前的编程式事务
①使用原生的JDBC API进行事务管理
[1]获取数据库连接Connection对象
[2]取消事务的自动提交
[3]执行操作
[4]正常完成操作时手动提交事务
[5]执行失败时回滚事务
[6]关闭相关资源
使用原生的JDBC API实现事务管理是所有事务管理方式的基石,同时也是最典型的编程式事务管理。编程式事务管理需要将事务管理代码嵌入到业务方法中来控制事务的提交和回滚。在使用编程的方式管理事务时,必须在每个事务操作中包含额外的事务管理代码。相对于核心业务而言,事务管理的代码显然属于非核心业务,如果多个模块都使用同样模式的代码进行事务管理,显然会造成较大程度的代码冗余。
3:Spring的声明时事务
3.1:导包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wkl</groupId>
<artifactId>Spring03Jdcbshiwu</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--ioc的包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--aop的包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--aop面向切面通知的包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!--c3p0的连接池包-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.1</version>
</dependency>
<!--mysql8的驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<!--导入jdbctemplate包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>
</project>
3.2:配置数据源
<?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:component-scan base-package="com.wkl"></context:component-scan>
<context:property-placeholder location="classpath:jdbcproperties.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="jdbcUrl" value="${jdbc.url}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
</beans>
3.3:写相关的逻辑操作
逻辑代码在此不再赘述,无非就是
1:创建dao放入容器
2:创建service放入容器
3:service调dao,执行若干设置jdbc的方法
3.4:配置声明式事务管理
快速的为某个方法添加事务:
1)、配置出这个事务管理器让他工作;
<!--配置事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--传入数据源-->
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
2)、开启基于注解的事务
名称空间
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--2:开启基于注解的事务控制模式;依赖tx名称空间 -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
3)、给事务方法加@Transactional注解
4:事务管理器的属性
4.1:timout-超时
超时事务属性:事务在强制回滚之前可以保持多久。这样可以防止长期运行的事务占用资源。
@Transactional(timeout = 3)
4.2:readOnly ---只读属性
只读事务属性: 表示这个事务只读取数据但不更新数据, 这样可以帮助数据库引擎优化事务。默认为false
@Transactional(readOnly = true)
4.3:触发事务回滚异常---noRollbackFor和rollbackFor
事务默认异常回滚机制:
- 捕获到RuntimeException(运行时异常)或Error时回滚,
- 而捕获到编译时异常(受检查异常)不回滚。
@Transactional 注解
[1]rollbackFor属性:指定默认不会滚,但是遇到时必须进行回滚的异常类型,可以为多个
[2]noRollbackFor属性:指定默认回滚,但是遇到时不回滚的异常类型,可以为多个
实例:
@Transactional(rollbackFor = {FileNotFoundException.class,NullPointerException.class},
noRollbackFor = {ArithmeticException.class})
4.4:事务隔离级别---数据库事务一个读,一个写就会发生问题;
1:数据库事务并发问题;假设现在有两个事务:Transaction01和Transaction02并发执行。
①脏读---读到了别人未提交的
[1]Transaction01将某条记录的AGE值从20修改为30。
[2]Transaction02读取了Transaction01更新后的值:30。
[3]Transaction01回滚,AGE值恢复到了20。
[4]Transaction02读取到的30就是一个无效的值。
②不可重复读---两次都得值不一样
[1]Transaction01读取了AGE值为20。
[2]Transaction02将AGE值修改为30。
[3]Transaction01再次读取AGE值为30,和第一次读取不一致。
③幻读---两次读的数据量不一样
[1]Transaction01读取了STUDENT表中的一部分数据。
[2]Transaction02向STUDENT表中插入了新的行。
[3]Transaction01读取了STUDENT表时,多出了一些行。
2:隔离级别;
数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
①读未提交:READ UNCOMMITTED
允许Transaction01读取Transaction02未提交的修改。
②读已提交:READ COMMITTED
要求Transaction01只能读取Transaction02已提交的修改。但在同一事务中有可能读到两次的值;
③可重复读:REPEATABLE READ
确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
④串行化:SERIALIZABLE
确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
3:各级别解决问题的能力;
脏读 | 不可重复读 | 幻读 | |
READ UNCOMMITTED(读未提交) | 有 | 有 | 有 |
READ COMMITTED(读已提交) | 无 | 有 | 有 |
REPEATABLE READ(可重复读) | 无 | 无 | 有 |
SERIALIZABLE(串行化) | 无 | 无 | 无 |
4:各种数据库产品对事务隔离级别的支持程度
Oracle | MySQL | |
READ UNCOMMITTED(读未提交) | × | √ |
READ COMMITTED(读已提交) | √(默认) | √ |
REPEATABLE READ(可重复读) | × | √(默认) |
SERIALIZABLE(串行化) | √ | √ |
5:用mysql演示
6:如果是两个修改的事务,那么在数据库底层就会自动排队,必须等一个修改完,另一个才执行;
4.5:事务的传播特性
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为。默认为REQUIRED
注:事务的传播特性针对的是内部的事务对于外部事务的反应
画图说明:
假如TX事务中调用了A和B两个事务
1:REQUIRED:该事务和他父事务坐一个车,并且事务属性都是继承父事务的,不能改变;
2:REQUIRED_NEW:改事务自己开车,不和他父亲做一个车;内部新开事务只是为了不让外边影响里边的;
4.6:事务传播性和隔离级别的区别
传播行为定义关于客户端和被调用方法的事务边界,
隔离级别定义一个事务可能受其他并发事务活动活动影响的程度
5:使用注解声明事务
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.wkl"></context:component-scan>
<context:property-placeholder location="classpath:jdbcproperties.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="jdbcUrl" value="${jdbc.url}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<!--1声明事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--传入数据源-->
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<!--2:开启基于注解的事务控制模式;依赖tx名称空间 -->
<!--<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>-->
<!--2:配置Aop代理,拦截需要绑定事务的方法-->
<aop:config>
<!--切入短表达式-->
<aop:pointcut id="myponint" expression="execution(public String com.wkl.BaseService.checkout())"/>
<!--配置事务建议,事务增强-->
<aop:advisor advice-ref="transactionInterceptor" pointcut-ref="myponint"></aop:advisor>
</aop:config>
<!--3:配置事务管理器 ,transaction-manager指定配置那个事务管理器-->
<tx:advice id="transactionInterceptor" transaction-manager="dataSourceTransactionManager">
<!--事务属性-->
<tx:attributes>
<!--指明那些方法是事务方法-->
<tx:method name="checkout" timeout="-1"/>
</tx:attributes>
</tx:advice>
</beans>