一 JdbcTemplate的使用
Spring对数据库的操作在jdbc上面做了深层次的封装,使用spring的注入功能,可以把DataSource注册到JdbcTemplate之中。 JdbcTemplate 是在JDBC API基础上提供了更抽象的封装,并提供了基于方法注解的事务管理能力。 通过使用SpringBoot自动配置功能并代替我们自动配置beans. 在maven中,我们需要增加spring-boot-starter-jdbc模块
org.springframework.boot
spring-boot-starter-jdbc
点开spring boot的自动配置包spring-boot-autoconfigure-1.5.2.RELEASE.jar,找到如下所示代码
发现spring boot已经自动帮我们初始化好了jdbcTemplate对象,并且默认采用tomcat数据源dataSource,当然我们提供的数据源,系统将采用我们自定义的数据源!
完整的pom文件如下所示:
4.0.0
com.wx
springboot02
0.0.1-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
1.5.2.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-jdbc
mysql
mysql-connector-java
runtime
org.apache.tomcat.embed
tomcat-embed-jasper
provided
jstl
jstl
1.2
com.alibaba
fastjson
1.1.15
com.alibaba
druid
1.0.9
${project.artifactId}
org.apache.maven.plugins
maven-compiler-plugin
1.7
UTF-8
org.springframework.boot
spring-boot-maven-plugin
好,接下来我们来看代码
sql脚本
CREATE TABLE users
(
userId INT PRIMARY KEY AUTO_INCREMENT,
userName VARCHAR(20) NOT NULL,
PASSWORD VARCHAR(20) NOT NULL,
email VARCHAR(50)
);
INSERT INTO users(userName,PASSWORD,email) VALUES('jack','123','[email protected]');
INSERT INTO users(userName,PASSWORD,email) VALUES('mike','123','[email protected]');
INSERT INTO users(userName,PASSWORD,email) VALUES('麻子','123','[email protected]');
CREATE TABLE account (
acctId INT(11) NOT NULL,
userName VARCHAR(20) NOT NULL,
balance DOUBLE DEFAULT NULL,
PRIMARY KEY (acctId)
)
INSERT INTO account VALUES(100,'麻子',2000);
INSERT INTO account VALUES(101,'小丽',2000);
数据源的配置类如下
package com.wx.boot;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import com.alibaba.druid.pool.DruidDataSource;
@Configuration
@PropertySource(value = { "classpath:druidConfig.properties",
"classpath:jdbc.properties" }, ignoreResourceNotFound = true)
public class DataSourceConfig {
@Value("${driverClassName}")
private String driverClassName;
@Value("${url}")
private String url;
@Value("${duridUserName}")
private String username;
@Value("${password}")
private String password;
@Value("${filters}")
private String filters;
@Value("${initialSize}")
private int initialSize;
@Value("${maxActive}")
private int maxActive;
@Value("${minIdle}")
private int minIdle;
@Value("${maxWait}")
private int maxWait;
@Value("${validationQuery}")
private String validationQuery;
@Value("${testWhileIdle}")
private boolean testWhileIdle;
@Value("${testOnBorrow}")
private boolean testOnBorrow;
@Value("${testOnReturn}")
private boolean testOnReturn;
@Value("${maxPoolPreparedStatementPerConnectionSize}")
private int maxPoolPreparedStatementPerConnectionSize;
@Value("${removeAbandoned}")
private boolean removeAbandoned;
@Value("${removeAbandonedTimeout}")
private int removeAbandonedTimeout;
@Value("${timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Bean(initMethod="init",destroyMethod="close")
public DruidDataSource dataSource(){
DruidDataSource dataSource=new DruidDataSource();
try {
dataSource.setUrl(url);
dataSource.setDriverClassName(driverClassName);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setFilters(filters);
dataSource.setInitialSize(initialSize);
dataSource.setMaxActive(maxActive);
dataSource.setMinIdle(minIdle);
dataSource.setMaxWait(maxWait);
dataSource.setValidationQuery(validationQuery);
dataSource.setTestWhileIdle(testWhileIdle);
dataSource.setTestOnBorrow(testOnBorrow);
dataSource.setTestOnReturn(testOnReturn);
dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
dataSource.setRemoveAbandoned(removeAbandoned);
dataSource.setRemoveAbandonedTimeout(removeAbandonedTimeout);
dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
System.out.println("连接池启动成功");
} catch (SQLException e) {
e.printStackTrace();
}
return dataSource;
}
}
其它配置请参考前面的文章
重点来看dao层代码
package com.wx.dao.user;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.wx.entitys.UserEntity;
@Repository("userDao")
public class UserDaoImpl implements IUserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
//判断用户是否登录成功
public boolean isLogin(UserEntity paramUser) {
boolean flag=false;
String sqlStr="select * from users where userName=? and passWord=?";
List userList=jdbcTemplate.query(sqlStr,
new Object[]{paramUser.getUserName(),paramUser.getPassWord()},
new BeanPropertyRowMapper(UserEntity.class));
if(userList!=null && userList.size()>0){
flag=true;
}
return flag;
}
//判断用户是否存在
public boolean isUserExist(String userName) {
boolean flag=false;
String sqlStr="select * from users where userName=?";
List userList=jdbcTemplate.query(sqlStr, new Object[]{userName},
new BeanPropertyRowMapper(UserEntity.class));
if(userList!=null && userList.size()>0){
flag=true;
}
return flag;
}
//保存用户
public void save(UserEntity user) {
String sqlStr="insert into users(userName,passWord,email) values(?,?,?)";
jdbcTemplate.update(sqlStr,
user.getUserName(),user.getPassWord(),user.getEmail());
}
}
二 事务问题
1. 传统事务处理方式
Spring支持使用注解配置声明式事务,所使用的注解是@Transactional。
首先仍然需要在Spring配置文件中配置事务管理类,并添加对注解配置的事务的支持,代码如示
经过如上配置,程序便支持使用@Transactional来配置事务了,代码如下所示:
public class UserService {
private UserDao userDao;
@Transactional(readOnly=false, propagation=Propagation.REQUIRES_NEW)
public void addUser(User user) {
this.userDao.save(user);
}
}
在业务方法上添加@Transactional就为该方法添加了事务处理,@Transactional中也可以设置事务属性的值,默认的@Transactional设置如下。
(1)事务传播设置是PROPACrATION_REQUIRED。
(2) 事务隔离级别是ISOLATION DEFAULT。
(3) 事务是读/写。
(4) 事务超时默认是依赖于事务系统的,或者事务超时没有被支持。
(5) 任何RuntimeException将触发事务回滚,但是任何checked Exception将不触发事务回滚。
这些默认的设置当然也是可以被改变的。@Transactional注解的各种属性设置总结如表所示。
表 @Transactional注解的属性
属 性 |
类 型 |
说 明 |
propagation |
枚举型:Propagation |
可选的传播性设置。使用举例:@Transacli onal(propagation=Propagation.REQUIRES_NEW) |
isolation |
枚举型:Esolation |
可选的隔离性级别。使用举例:@Transactional(isolation=Isolation.READ COMMITTED) |
readOnly |
布尔型 |
是否为只读型事务。使用举例: @Trans actional(readOnly=true) |
tlmeoUt |
int型(以秒为单位) |
事务超时。使用举例:@T ransactional(timeout=1 0) |
roIlbackFor |
一组Class类的实例,必须是Throwable的予类 |
一组异常类,遇到时必须进行圆滚。使用举例: @Transactional(rollbackFor={S QLException.class}),多个异常可用英文逗号隔开 |
rollbackForClassName |
一组Class类的名称,必须是Throwable的子类 |
一组异常类名,遇到时必须进行圆滚。使用举例: @Transactional(rollbackForClassName={”SQLException”}),多个异常可用英文逗号隔开 |
noRollbackFor |
一组Class类的实例,必须是Throwable的子类 |
一组异常类,遇到时必须不圆滚 |
noRollbackForClassName |
一组Class类的名字,必须是Throwable的子类 |
一组异常类名,遇到时必须不圆滚 |
2. spring boot事务处理方式
在Spring Boot中推荐使用@Transactional注解来申明事务。
首先需要导入依赖:
org.springframework.boot
spring-boot-starter-jdbc
当引入jdbc依赖之后,Spring Boot会自动默认分别注入DataSourceTransactionManager或JpaTransactionManager,所以我们不需要任何额外配置就可以用@Transactional注解进行事务的使用。spring-boot-starter-jdbc会触发DataSourceTransactionManagerAutoConfiguration这个自动化配置类,会构造事务管理器,如下图:
在业务类中添加@Transactional注解:
package com.wx.biz;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.wx.dao.account.IAccountDao;
@Service("acctBiz")
public class AccountBiz {
@Autowired
private IAccountDao acctDao;
//把转账封装成一个方法,保证事务的原子性
@Transactional
public void doTransfer(Integer srcUserId,Integer targetUserId,double amount){
acctDao.doPay(srcUserId, amount);
acctDao.doReceive(targetUserId, amount);
}
@Transactional
public void doPay(Integer userId, double amount){
acctDao.doPay(userId, amount);
}
@Transactional
public void doReceive(Integer userId, double amount){
acctDao.doReceive(userId, amount);
}
}
我们也可以在dao层添加事务,下面来演示事务隔离级别
package com.wx.dao.account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.wx.entitys.AccountEntity;
@Repository(value="acctDao")
public class AcountDaoImpl implements IAccountDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@SuppressWarnings("unchecked")
//演示事务隔离级别,每次开启一个新事务,而不是使用已经开启的事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
public int doPay(Integer acctId, double money) {
int result=0;
String sqlStr="update account set balance=balance-? where userId=?";
result=jdbcTemplate.update(sqlStr, money,acctId);
if(result==0){
throw new RuntimeException("支付失败...");
}
return result;
}
@SuppressWarnings("unchecked")
@Transactional(propagation=Propagation.REQUIRES_NEW)
public int doReceive(Integer acctId, double money) {
int result=0;
String sqlStr="update account set balance=balance+? where userId=?";
result=jdbcTemplate.update(sqlStr, money,acctId);
if(result==0){
throw new RuntimeException("收款失败...");
}
return result;
}
}
@Transactional不仅可以注解在方法上,也可以注解在类上。当注解在类上的时候意味着所有的public方法都是开启事务的。