spring 提供的用于操作JDBC工具类,类似:DBUtils。依赖 连接池DataSource (数据源)
create database ee19_spring_day02;
use ee19_spring_day02;
create table t_user(
id int primary key auto_increment,
username varchar(50),
password varchar(32)
);
insert into t_user(username,password) values('jack','1234');
insert into t_user(username,password) values('rose','5678');
public class User {
private Integer id;
private String username;
private String password;
//get set
}
public class TestApi {
public static void main(String[] args) {
//1 创建数据源(连接池) dbcp
BasicDataSource dataSource = new BasicDataSource();
// * 基本4项
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/ee19_spring_day02");
dataSource.setUsername("root");
dataSource.setPassword("1234");
//2 创建模板
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
//3 通过api操作
jdbcTemplate.update("insert into t_user(username,password) values(?,?);", "tom","998");
}
}
说明:执行该部分代码即可向数据库中添加数据
开发中数据库连接池在spring配置文件中进行配置,这里新建一个dao用来封装数据库操作:
public class UserDao {
//jdbc模板将由spring注入
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
//更新操作
public void update(User user){
String sql = "update t_user set username=?,password=? where id =?";
Object[] args = {user.getUsername(),user.getPassword(),user.getId()};
jdbcTemplate.update(sql, args);
}
// 查询所有
public List findAll() {
return jdbcTemplate.query("select * from t_user", ParameterizedBeanPropertyRowMapper.newInstance(User.class));
}
//根据id查询
public User getById(int id) {
return this.getJdbcTemplate().queryForObject("select * from t_user where id = ?", ParameterizedBeanPropertyRowMapper.newInstance(User.class), id);
}
}
说明:ParameterizedBeanPropertyRowMapper.newInstance(User.class)用来将查询数据结果封装到bean中。
那么它的测试类如下:
public class TestDBCP {
@Test
public void demo01(){
User user = new User();
user.setId(1);
user.setUsername("接客");
user.setPassword("998");
String xmlPath = "com/itheima/c_dbcp/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//获得目标类
UserDao userDao = (UserDao) applicationContext.getBean("userDaoId");
userDao.update(user);
}
}
需要更改的部分只有创建数据源部分:
那么它的测试类如下:
@Test
public void demo01(){
String xmlPath = "com/itheima/d_c3p0/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserDao userDao = (UserDao) applicationContext.getBean("userDaoId");//获得目标类
List allUser = userDao.findAll();
for (User user : allUser) {
System.out.println(user);
}
}
在Dao中每次都要自己创建jdbcTemplate模板,比较繁琐,可以将Dao继承JdbcDaoSupport:
public class UserDao extends JdbcDaoSupport{
public void update(User user){
String sql = "update t_user set username=?,password=? where id =?";
Object[] args = {user.getUsername(),user.getPassword(),user.getId()};
this.getJdbcTemplate().update(sql, args);//直接调用父类的
}
//。。。。
}
在配置文件中也可以简化:
可以将数据库配置信息放在properties文件(jdbcInfo.properties)中:
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ee19_spring_day02
jdbc.user=root
jdbc.password=1234
配置文件更改为:
平台事务管理器,spring要管理事务,必须使用事务管理器(进行事务配置时,必须配置事务管理器)。事务管理器是通过“事务详情”,获得“事务状态”,从而管理事务。
常见的事务管理器有(即其实现类):
事务详情(又称事务定义、事务属性),spring用于确定事务具体详情(例如:隔离级别、是否只读、超时时间 等)。进行事务配置时,必须配置详情。spring将配置项封装到该对象实例。
其中的方法有:
getPropagationBehavior() | 传播行为 |
getIsolationLevel() | 隔离级别 |
getTimeout() | 获取超时时间 |
isReadOnly() | 是否只读(增删改 读写,查 只读) |
getName | 配置事务详情名称。一般方法名称。例如:save |
其中的常量有:
TIMEOUT_DEFAULT:默认值为-1,代表的是使用数据库底层的超时时间
ISOLATION_DEFAULT
ISOLATION_READ_UNCOMMITED
ISOLATION_READ_COMMITED
ISOLATION_REPEATABLE_READ
ISOLATION_SERIALIZABLE
PROPAGATION_REQUIRED :required , 必须 【默认值】
=》支持当前事务,A如果有事务,B将使用该事务。
=》如果A没有事务,B将创建一个新的事务。
PROPAGATION_SUPPORTS :supports ,支持
=》支持当前事务,A如果有事务,B将使用该事务。
=》如果A没有事务,B将以非事务执行。
PROPAGATION_MANDATORY,mandatory ,强制
=》支持当前事务,A如果有事务,B将使用该事务。
=》如果A没有事务,B将抛异常。
PROPAGATION_REQUIRES_NEW , requires_new ,必须新的
=》如果A有事务,将A的事务挂起,B创建一个新的事务
=》如果A没有事务,B创建一个新的事务
PROPAGATION_NOT_SUPPORTED ,not_supported ,不支持
=》如果A有事务,将A的事务挂起,B将以非事务执行
=》如果A没有事务,B将以非事务执行
PROPAGATION_NEVER ,never,从不
=》如果A有事务,B将抛异常
=》如果A没有事务,B将以非事务执行
PROPAGATION_NESTED ,nested ,嵌套
=》A和B底层采用保存点机制,形成嵌套事务。
掌握:PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED
事务状态,spring用于记录当前事务运行状态(例如:是否有保存点,事务是否完成)。spring底层根据状态进行相应操作。其方法有:
isNewTransaction() | 是否是新事务 |
hasSavepoint() | 是否有保存点 |
setRollbackOnly() | 设置回滚 |
isRollbackOnly() | 是否回滚 |
flush() | 刷新 |
isCompleted() | 是否完成 |
以银行转账为例,首先创建表如下:
create database ee19_spring_day03;
use ee19_spring_day03;
create table account(
id int primary key auto_increment,
username varchar(50),
money int
);
insert into account(username,money) values('jack','10000');
insert into account(username,money) values('rose','10000');
之后dao层为:
public interface AccountDao {
public void out(String outer , Integer money);//汇款
public void in(String inner , Integer money);// 收款
}
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
public void out(String outer, Integer money) {
this.getJdbcTemplate().update("update account set money = money - ? where username = ?", money,outer);
}
public void in(String inner, Integer money) {
this.getJdbcTemplate().update("update account set money = money + ? where username = ?", money,inner);
}
}
service层则为:
public interface AccountService {
public void transfer(String outer ,String inner ,Integer money);//转账
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
//转账实现
public void transfer(String outer, String inner, Integer money) {
accountDao.out(outer, money);
// int i = 1/0;//断电
accountDao.in(inner, money);
}
}
而在spring中的配置:
在测试类中:
@Test
public void demo01(){
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService = (AccountService) applicationContext.getBean("accountService");
accountService.transfer("jack", "rose", 1000);
}
运行如上代码转账成功,现在开始在上述代码中添加事务操作。
spring底层使用 TransactionTemplate 事务模板进行操作。因此可以自己通过TransactionTemplate来进行管理事务具体步骤如下:
1.service 需要获得 TransactionTemplate,那么service代码修改为:
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
//需要spring注入模板
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
public void transfer(final String outer,final String inner,final Integer money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {//事务放在这里
accountDao.out(outer, money);
// int i = 1/0;//断电
accountDao.in(inner, money);
}
});
}
}
2.在spring配置文件中需要配置模板,并注入给service,模板需要注入事务管理器,配置事务管理器(需要注入DataSource)那么在xml配置文件中新加:
即可完成事务添加
spring提供一个管理事务的代理工厂bean TransactionProxyFactoryBean。那么在配置文件中配置其代理工厂(之前不变):
PROPAGATION_REQUIRED,ISOLATION_DEFAULT
说明:在使用代理工厂时:
prop.key :确定哪些方法使用当前事务配置
prop.text:用于配置事务详情
格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
传播行为 隔离级别 是否只读 异常回滚 异常提交
例如:
之后再测试类中获取的是代理对象,而不是原对象:
@Test
public void demo01(){
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService = (AccountService) applicationContext.getBean("proxyAccountService");
accountService.transfer("jack", "rose", 1000);
}
在spring 配置文件中配置aop 自动生成代理,进行事务的管理。
采用注解方式需要在spring配置中添加新的命名空间,及配置:
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
之后在目标类或目标方法添加注解即可 @Transactional
@Transactional()
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outer, String inner, Integer money) {
accountDao.out(outer, money);
int i = 1/0;//断电
accountDao.in(inner, money);
}
}
注意:之前的事务详情怎么配,在@Transactional中添加参数即可,如:
@Transactional(propagation=Propagation.REQUIRED , isolation = Isolation.DEFAULT)