Spring学习9-JdbcTemplate及事务控制

什么是JdbcTemplate

首先这里看一张图

持久层总图

JdbcTemplate概述

它是spring框架中提供的一个对象,是对原始Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。

  • 操作关系型数据的: JdbcTemplate HibernateTemplate

  • 操作nosql数据库的: RedisTemplate

  • 操作消息队列的: JmsTemplate

我们今天的主角在spring-jdbc-5.2.0.RELEASE.jar中,我们在导包的时候,除了要导入这个jar包外,还需要导入一个spring-tx-5.2.0.RELEASE.jar(它是和事务相关的)。

创建与使用

最基本方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.gsynf.jdbctemplate;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

/**
* @ClassName: JdbcTemplateDemo1
* @Description: JdbcTemplate最基本用法
*/
public class JdbcTemplateDemo1 {
public static void main(String[] args) {
// 准备数据源:spring的内置数据源
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://服务器IP:3306/spring5?useSSl:false");
ds.setUsername("账户");
ds.setPassword("密码");

// 1.创建JdbcTemplate对象
JdbcTemplate jt = new JdbcTemplate();
// 给jt设置数据源
jt.setDataSource(ds);
// 2.执行操作
jt.execute("insert into account (name,money)values('ddd',1000)");


}
}

使用IoC配置

1
2
3
4
5
6
7
8
9
10
11
<!--配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置dataSource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://服务器IP:3306/spring5?useSSL=false"></property>
<property name="username" value="账户"></property>
<property name="password" value="密码"></property>
</bean>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.gsynf.jdbctemplate;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

/**
* @ClassName: JdbcTemplateDemo2
* @Description: JdbcTemplate的IoC配置
*/
public class JdbcTemplateDemo2 {
public static void main(String[] args) {

// 1. 获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
// 2. 获取对象
JdbcTemplate jt = ac.getBean("jdbcTemplate", JdbcTemplate.class);
// 3. 执行操作
jt.execute("insert into account (name,money)values('eee',1000)");
}
}

实现CRUD操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.gsynf.jdbctemplate;

import com.gsynf.domain.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
* @ClassName: JdbcTemplateDemo3
* @Description: JdbcTemplateCRUD操作
*/
public class JdbcTemplateDemo3 {
public static void main(String[] args) {

// 1. 获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
// 2. 获取对象
JdbcTemplate jt = ac.getBean("jdbcTemplate", JdbcTemplate.class);
// 3. 执行操作
//保存
// jt.update("insert into account(name,money)values(?,?)","fff",3333f );
//更新
// jt.update("update account set name=?,money=? where id=?","test",4567,5);
//删除
// jt.update("delete from account where id = ?",7);
//查询所有
//// List<Account> accounts = jt.query("select * from account where money > ?",new AccountRowMapper(),1000f);
// List<Account> accounts = jt.query("select * from account where money > ?",new BeanPropertyRowMapper<Account>(Account.class),1000f);
// for (Account account : accounts){
// System.out.println(account);
// }
//查询一个
// List<Account> accounts = jt.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),5);
// System.out.println(accounts.isEmpty()?"没有内容":accounts.get(0));
//查询返回一行一列(使用聚合函数,但不加group by子句)
Long count = jt.queryForObject("select count(*) from account where money > ?",Long.class,1000);
System.out.println(count);
}
}

/**
* 定义Account的封装策略
*/
class AccountRowMapper implements RowMapper<Account> {
/**
* 把结果集中的数据封装到Account中,然后由spring把每个Account加到集合中
* @param resultSet
* @param i
* @return
* @throws SQLException
*/
@Override
public Account mapRow(ResultSet resultSet, int i) throws SQLException {
Account account = new Account();
account.setId(resultSet.getInt("id"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getFloat("money"));
return account;
}
}

在Dao中的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.gsynf.dao.impl;

import com.gsynf.dao.IAccountDao;
import com.gsynf.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;

/**
* @ClassName: AccountDaoImpl
* @Description: 持久层接口实现类
*/
public class AccountDaoImpl implements IAccountDao {
private JdbcTemplate jdbcTemplate;

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

@Override
public Account findAccountById(Integer accountId) {
List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
return accounts.isEmpty()?null:accounts.get(0);
}

@Override
public Account findAccountByName(String accountName) {
List<Account> accounts = jdbcTemplate.query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);
if (accounts.isEmpty()) {
return null;
}
if (accounts.size() > 1) {
throw new RuntimeException("结果集不唯一");
}
return accounts.get(0);
}

@Override
public void updateAccount(Account account) {
jdbcTemplate.update("update account set name=?,money=? where id = ?",account.getName(),account.getMoney(),account.getId());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.gsynf.jdbctemplate;

import com.gsynf.dao.IAccountDao;
import com.gsynf.domain.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* @ClassName: JdbcTemplateDemo4
* @Description: JdbcTemplate的IoC配置
*/
public class JdbcTemplateDemo4 {
public static void main(String[] args) {

// 1. 获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
// 2. 获取对象
IAccountDao accountDao = ac.getBean("accountDao",IAccountDao.class);

Account account = accountDao.findAccountById(1);
System.out.println(account);

account.setMoney(1000f);
accountDao.updateAccount(account);
}
}

Spring中的事务控制

Spring事务控制要明确的

  • 第一:JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案。

  • 第二:spring框架为我们提供了一组事务控制的接口。具体在后面介绍。这组接口是在spring-tx-5.0.2.RELEASE.jar中。

  • 第三:spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,也可以使用配置的方式实现。我们学习的重点是使用配置的方式实现。

基于XML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--配置业务层-->
<bean id="accountService" class="com.gsynf.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>

<!--配置账户的持久层-->
<bean id="accountDao" class="com.gsynf.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>

<!--配置dataSource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://服务器IP:3306/spring5?useSSL=false"></property>
<property name="username" value="账户"></property>
<property name="password" value="密码"></property>
</bean>

<!--spring中基于XML的声明式事务管理配置步骤
1.配置事务管理器
2.配置事务的通知: 此时需要导入tx的名称和约束,同时也需要AOP的
3.配置AOP中的通用切入点表达式
4.建立事务通知和切入点表达式的对应关系
5.配置事务的属性:在事务的通知tx:advice标签内部
-->
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--配置事务属性
isolation:指定事务的隔离级别,默认值是DEFAULT,表示使用数据库的默认隔离级别;
propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS;
read-only:用于指定事务是否只读。只有查询方法才能设为true,默认为false;
timeout:用于指定事务的超时时间,默认值是-1,表示永不超时,如指定数值,单位为秒;
no-rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚;
rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回滚。没有默认值。表示任何异常都回滚;
-->
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>

<!--配置AOP-->
<aop:config>
<!--配置切入点表达式-->
<aop:pointcut id="pt1" expression="execution(* com.gsynf.service.impl.*.*(..))"/>
<!--建立切入点表达式和事务通知的对应关系-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
</beans>

基于注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<!--配置spring创建容器时要扫描的包-->
<context:component-scan base-package="com.gsynf"></context:component-scan>

<!--配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>

<!--配置dataSource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://服务器IP:3306/spring5?useSSL=false"></property>
<property name="username" value="账户"></property>
<property name="password" value="密码"></property>
</bean>

<!--spring中基于注解的声明式事务管理配置步骤
1.配置事务管理器
2.开启spring对注解事务的支持
3.在需要事务支持的地方使用@Transactional注解
-->
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

<!--开启spring对注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!--配置AOP-->
<aop:config>
<!--配置切入点表达式-->
<aop:pointcut id="pt1" expression="execution(* com.gsynf.service.impl.*.*(..))"/>
<!--建立切入点表达式和事务通知的对应关系-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.gsynf.service.impl;

import com.gsynf.dao.IAccountDao;
import com.gsynf.domain.Account;
import com.gsynf.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
* @ClassName: AccountService
* @Description: 业务层实现类
*/
@Service("accountService")
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
public class AccountServiceImpl implements IAccountService {

@Autowired
private IAccountDao accountDao;

@Override
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}

@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
@Override
public void transfer(String sourceName, String targetName, Float money) {
System.out.println("transfer…………");
// 2.1根据名称查询转入账户
Account source = accountDao.findAccountByName(sourceName);
// 2.2根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
// 2.3转出账户减钱
source.setMoney(source.getMoney() - money);
// 2.4转入账户加钱
target.setMoney(target.getMoney() + money);
// 2.5更新转出账户
accountDao.updateAccount(source);

int i = 1/0; //故意加错,模拟转账异常

// 2.6更新转入账户
accountDao.updateAccount(target);
}
}

基于纯注解

和基于注解相比,主要变化:

新建config包,下有SpringConfiguration/JdbcConfig/TransactionConfig,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
* @ClassName: SpringConfiguration
* @Description: spring的配置类,相当于bean.xml
*/
@Configuration
@ComponentScan("com.gsynf")
@Import({JdbcConfig.class,TransactionConfig.class})
@PropertySource("jdbcConfig.properties")
@EnableTransactionManagement
public class SpringConfiguration {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;

/**
* @ClassName: JdbcConfig
* @Description: 和连接数据库相关的配置类
*/
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 创建JdbcTemplate对象
* @param dataSource
* @return
*/
@Bean(name = "jdbcTemplate")
public JdbcTemplate createJdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}

/**
* 创建一个数据源对象
* @return
*/
@Bean(name = "dataSource")
public DataSource createDataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package config;

import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

/**
* @ClassName: TransactionConfig
* @Description: 事务控制配置类
*/
public class TransactionConfig {

/**
* 用于创建事务管理器对象
* @param dataSource
* @return
*/
@Bean(name = "transactionManager")
public PlatformTransactionManager createCransactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}

resource下新建JdbcConfig.properties,

1
2
3
4
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://服务器IP:3306/spring5?useSSL=false
jdbc.username=账户
jdbc.password=密码

Test中修改

1
@ContextConfiguration(classes = SpringConfiguration.class)

:转载文章请注明出处,谢谢~