SpringJDBCTemplate
什么是JDBCTemplate
Spring框架对JDBC进行了封装,使用这个可以方便的进行JDBC操作
但是之后会存在数据库的框架Mybatis,实际中也不会使用JDBC
示例操作
引入依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
Spring配置文件
<?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: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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="cn"/>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/usershow?characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="ok"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<bean id= "jdbcTemplate" class= "org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
javaBean
java连接数据库,为了方便操作数据库中的数据
将每一个表对应javaBean进行映射,这里可以使用IDEA的连接数据库生成
进行指定连接数据库,选择具体的库
指定数据表自定创建javaBean
package cn.pojo;
public class User {
private long id;
private String cname;
private String mobile;
private String qq;
private String email;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getQq() {
return qq;
}
public void setQq(String qq) {
this.qq = qq;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
测试是否连接成功
daoImple类,这里并没有使用规范,编写接口,编写实现等操作.
而是直接在数据访问层中一气呵成
import java.util.List;
import java.util.Map;
@Repository("daoImple")
public class DaoImple {
@Autowired
private JdbcTemplate jdbcTemplate;
public void get(){
String sql ="SELECT * FROM user";
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
System.out.println("查询到总记录数"+maps.size());
System.out.println("第一个查询结果的cname属性值"+maps.get(0).get("cname"));
}
}
public class SpringIOCTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("SpringTest.xml");
DaoImple daoImple = context.getBean("daoImple", DaoImple.class);
daoImple.get();
}
}
JDBCTemplate在一定基础上简化了JDBC的开发.
SQL与程序在一起,存在耦合.之后会使用Mybatis框架进行优化
Mysql可以做到SQL与代码分离.通过ORM对象关系映射.
JDBATemplate的API
上面通过向Spring容器中添加数据源,添加JDBCTemplat已经实现可以操作数据库.下面就介绍该类的具体API
方法 | 描述 |
---|
execute(String sql) | 可执行任何sql语句,但返回值是void,所以一般用于数据库的新建、修改、删除和数据表记录的增删改。 | int update(String sql) | 增删改,args传递实参,返回受影响的记录数。 | int update(String sql, Object…args) | 增删改,args传递实参,返回受影响的记录数。 | List query(String sql, PreparedStatementSetter pss, RowMapper rowMapper) | 该方法根据String类型的参数提供的SQL语句创建PreparedStatement对象,通过RowMapper将结果返回到List中。 | List query(String sql, Object[] args, RowMapper rowMapper) | 该方法使用Object[] 的值来设置SQL中的参数值,采用RowMapper回调方法可以直接返回List类型的数据。 | queryForObject(String sql, Object[] args, RowMapper rowMapper) | 该方法将args参数绑定到SQL语句中,通过RowMapper返回单行记录,并转换为一个Object对象返回 | queryForList(String sql,Object[] args, classelementType) | 该方法可以返回多行数据的结果,但必须是返回列表,elementType参数返回的是List元素类型。 |
增删改
1 1 、修改
@Override
public void updateBook(Book book){
String sql="update t_book set username=?,ustatus=? where user_id=?";
Object[]args={book.getUsername(),book.getUstatus(),book.getUserId()};
int update=jdbcTemplate.update(sql,args);
System.out.println(update);
}
2 2 、删除
@Override
public void delete(String id){
String sql="delete from t_book where user_id=?";
int update=jdbcTemplate.update(sql,id);
System.out.println(update);
}
查询
Spring事物
什么是事物
(1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操 作都失败 (2)典型场景:银行转账
- lucy 转账 100 元 给 mary
- lucy 少 100,mary 多 100
事物四个特性
- 一致性:就是说事物中的操作要么成功,要么都失败。
- 原子性:个人感觉其实与一致性是差不多的感觉,也是为了保证要么都成功,要么都失败。
- 隔离性:事物与事物直接是要相互不能够影响的。
- 持久性:事物成功,对数据库中操作的数据的改变是永久性的。
数据库的五种隔离级别
- Default:使用数据库默认的事务隔离级别
- Read_uncommited:未提交读,:脏读,不可重复读,虚读都有可能发生
- Read_conmmited:已提交读,不可重复读,虚读都有可能发生
- Repeatable_read:可重复读 虚读都有可能发生
- Serializeble:串化,锁表
七种传播行为
什么是传播行为:当一个事务调用两一个事务时,即A方法调用B方法时,并且A、B方法都具有事务的隔离级别,那么这现象就叫做传播行为。
Spring 支持 7 种事务传播行为(Transaction Propagation Behavior):
(1)必须有事务
- **PROPAGATION_REQUIRED :**如果没有,就开启一个事务;如果有,就加入当前事务(方法B看到自己已经运行在 方法A的事务内部,就不再起新的事务,直接加入方法A)
- **RROPAGATION_REQUIRES_NEW :**如果没有,就开启一个事务;如果有,就将当前事务挂起。(方法A所在的事务就会挂起,方法B会起一个新的事务,等待方法B的事务完成以后,方法A才继续执行)
- **PROPAGATION_NESTED:**如果没有,就开启一个事务;如果有,就在当前事务中嵌套其他事务
- **PROPAGATION_MANDATORY:**如果没有,就抛出异常;如果有,就使用当前事务
(2)事务可有可无
PROPAGATION_SUPPORTS: 如果没有,就以非事务方式执行;如果有,就加入当前事务(方法B看到自己已经运行在 方法A的事务内部,就不再起新的事务,直接加入方法A)
(3)不需要事务
- **PROPAGATION_NOT_SUPPORTED :**如果没有,就以非事务方式执行;如果有,就将当前事务挂起,(方法A所在的事务就会挂起,而方法B以非事务的状态运行完,再继续方法A的事务)
- **PROPAGATION_NEVER :**如果没有,就以非事务方式执行;如果有,就抛出异常。
小结
① 在多线程中配置事务的隔离级别,和传播特性之后,在产生事务挂起的情况时,会产生死锁,这时候需要配置超时时间,以便尽快熔断。 ② 在同一个类里面,如果事务A方法通过this调用方法B事务时,在不同的情况下可能会出现,B方法的事务不会被切入进去,这时候可以将这个类暴露出去,在事务A中通过类来来调用事务B。 ③ 方法A有事务,方法B没有事务,方法A调用方法B,B加入A的事务中。
事物操作示例
没有进行事物
我的数据库
我的Dao与Service
javaBean修改了
public class User {
private long id;
private String cname;
private String mobile;
private String qq;
private double money;
}
Dao层实现类
@Repository("userdaoimple")
public class UserDaoImple implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void subtractMethod(int id, int Money) {
String sql = " UPDATE USER SET money = money-? WHERE id =? ";
jdbcTemplate.update(sql,Money,id);
}
public void addMethod(int id, int Money) {
String sql = " UPDATE USER SET money = money+? WHERE id =? ";
jdbcTemplate.update(sql,Money,id);
}
}
Service实现类
@Service("userservice")
public class UserServiceImple implements UserService {
@Autowired
private UserDao userDao;
public void txMoney(int id1, int id2, int Money) {
userDao.subtractMethod(id1,Money);
userDao.addMethod(id2,Money);
}
}
第一次执行
发现确实没有错误.
但是如果在执行Service的时候出现异常.那么结果
修改Service代码,使在中途出现异常. 那么结果就会出现,一部分SQL执行,一部分SQL不执行
public void txMoney(int id1, int id2, int Money) {
userDao.subtractMethod(id1,Money);
// 在这里制造一个算数异常
int a= 10/0;
userDao.addMethod(id2,Money);
}
发现id1减少,id2没有增加
Spring 进行事务管理操作
有两种方式:编程式事务管理和声明式事务管理(使用)
编程式事物
声明式事物
- 基于注解方式
- 基于xml 配置文件方式
在 Spring 进行声明式事务管理,底层使用 AOP 原理 Spring 事务管理 API 提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类 事务操作
Spring通过接口进行事物的处理,分别提供的实现类,对于不同的框架使用不同的实现类
JDBC和Mybatis都是使用DataSourceTransactionManager
声明式基于注解操作
在Spring配置文件中添加事物管理器和开启事物
<bean id= "transactionManager"
class= "org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name= "dataSource" ref= "dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
在 service 类上面( 类上面(或者 或者 service 类里面方法上面)添加事务注解 (1)@Transactional,这个注解添加到类上面,也可以添加方法上面 (2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务 (3)如果把这个注解添加方法上面,为这个方法添加事务
@Service("userservice")
@Transactional()
public class UserServiceImple implements UserService {
@Autowired
private UserDao userDao;
public void txMoney(int id1, int id2, int Money) {
userDao.subtractMethod(id1,Money);
int a= 10/0;
userDao.addMethod(id2,Money);
}
}
发现SQL要执行都执行
事物操作(声明式事物的管理参数)
在@Transactional注解中可以添加参数
事物传播行为(propagation)
多个事物方法直接进行调用,这个过程中事物是如何进行实现的
在业务逻辑层中,每个方法都是事物,当业务逻辑中,方法进行的调用,那么事物如何进行传播
事物的隔离级别
事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题
1、未提交读(Read uncommitted)
①定义:就是一个事务读取到其他事务未提交的数据,是级别最低的隔离机制。
②缺点:会产生脏读、不可重复读、幻读。
③案例解读:以前交易所炒股的时候,股民老王购买了5000股,操作员操作录入(此时开启事务),操作时手误,多输入了一个0,数据保存但是未提交。此时老王查询自己的持股信息,发现自己居然有50000股,瞬间血压升高,昏倒在地。然后操作员发现自己录入错误,修改成正确的信息,录入完成(事务结束)。老王被救醒后,哆嗦这查询自己的持股,发现只有5000,没有增减,他之前看到的就是脏读数据。
④解决方案:采用更高级的隔离机制,如提交读。
2、提交读(Read committed)
①定义:就是一个事务读取到其他事务提交后的数据。Oracle默认隔离级别。
②缺点:会产生不可重复读、幻读。
③案例解读:股市升高后,老王查看自己持有5000股,就想卖掉4000股,在老王卖股票的时候,老王的老婆看股市太高,就登录老王的账号,卖掉3000股。当老王想卖股票时,发现自己只有2000股,不是之前看到的5000股,这就是不可重复读问题。
④解决方案:采用更高级的隔离机制,如可重复读。
3、可重复读(Repeatable read)
①定义:就是一个事务对同一份数据读取到的相同,不在乎其他事务对数据的修改。MySQL默认的隔离级别。
②缺点:会产生幻读。
③问题解读:股市忽涨忽跌,老王焦虑不安,按捺不住,想把持有的多种股票全部抛掉。与此同时,老外老婆听信砖家所言,使用老王的账号买了某只神股。老王抛掉所有股票后,查看自己的持股,猛然发现自己居然还持有一只股票,瞬间觉得一脸懵逼,这就是幻读导致。
④解决方案:采用更高级的隔离机制,序列化。
4、序列化(Serializable)
①定义:事务串行化执行,隔离级别最高,牺牲了系统的并发性。
②缺点:可以解决并发事务的所有问题。但是效率地下,消耗数据库性能,一般不使用。
?
timeout :超时时间
(1)事务需要在一定时间内进行提交,如果不提交进行回滚 (2)默认值是 -1 ,设置时间以秒单位进行计算
readOnly :是否只读
(1)读:查询操作,写:添加修改删除操作 (2)readOnly 默认值 false,表示可以查询,可以添加修改删除操作 (3)设置 readOnly 值是 true,设置成 true 之后,只能查询
rollbackFor :回滚
(1)设置出现哪些异常进行事务回滚
noRollbackFor :不回滚
(1)设置出现哪些异常不进行事务回滚
下面还存在Spring对于事物的XML和全注解。就不多介绍了
|