Spring 学习4
JdbcTemplate
学习 JdbcTemplate,它是 Spring 提供的一个对象,是对原始 Jdbc API 对象的简单封装,Spring 为我们提供了很多的操作模板类。
操作关系型数据的: JdbcTemplate HibernateTemplate
操作 nosql 数据库的: RedisTemplate
操作消息队列的: JmsTemplate
pom.xml 文件添加依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!--jdbc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--事务相关依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
</dependencies>
创建演示数据库表
CREATE DATABASE `spring_note`;
USE spring_note;
CREATE TABLE `t_account`(
`id` INT(8) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) NOT NULL,
`money` FLOAT(8) NOT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
创建实体类
public class Account implements Serializable {
private int id;
private String name;
private float money;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getMoney() {
return money;
}
public void setMoney(float money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
数据库操作要用到数据源,如c3p0,Druid等,Spring 提供了一个内置数据源,这里就用内置数据源演示。
public class AppNoteDemo {
public static void main(String[] args) {
//Spring 内置数据源
DriverManagerDataSource dataSource=new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring_note");
dataSource.setUsername("root");//数据库用户名
dataSource.setPassword("root");//数据库密码
//创建JdbcTemplate对象
JdbcTemplate jdbcTemplate=new JdbcTemplate();
//给jdbcTemplate设置数据源
jdbcTemplate.setDataSource(dataSource);
//执行操作,这里演示插入数据操作
jdbcTemplate.execute("insert into t_account(name,money) values('apple',100)");
}
}
(id为2是因为之前删了1的数据,不重要)
写在 java 代码里的配置信息可以写进 XML 配置文件里的
bean.xml
<?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: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/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">
<!--配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置数据源-->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_note"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
</beans>
启动类
public class AppNoteDemo {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
JdbcTemplate jdbcTemplate=ac.getBean("jdbcTemplate",JdbcTemplate.class);
//执行操作,这里演示插入数据操作
jdbcTemplate.execute("insert into t_account(name,money) values('a1',100)");
}
}
JdbcTemplate 简易增删查改操作
增加
public class AppNoteDemo {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
JdbcTemplate jdbcTemplate=ac.getBean("jdbcTemplate",JdbcTemplate.class);
//执行操作,这里演示插入数据操作
jdbcTemplate.execute("insert into t_account(name,money) values('a1',100)");
}
}
更新
jdbcTemplate.update("update t_account set money=? where id=?",200,6);
删除
jdbcTemplate.update("delete from t_account where id=?",6);
查询全部
List<Account> accountList=jdbcTemplate.query("select * from t_account where money> ?",new BeanPropertyRowMapper<Account>(Account.class),100);
参数2:<Account>泛型为你查询到的数据对应实体类,(Account.class) 为你数据对应实体类的字节码
参数3:参数1 SQL 语句的?位置的数据
查询1条数据
List<Account> accountList=jdbcTemplate.query("select * from t_account where id= ?",new BeanPropertyRowMapper<Account>(Account.class),4);
System.out.println(accountList.get(0));
返回1条数据,所以列表里第0个数据就是查到的那1条数据。
查询1行1列,使用聚合函数但不加group by语句
int returnValue=jdbcTemplate.queryForObject("select count(*) from t_account where money > ?",Integer.class,100);
参数2:你查询的数据是什么类型就用什么类型的字节码,这里查询到的是个整数就用 Integer.class
参数3:参数1 SQL 语句的?位置的数据
JdbcTemplate 常用增删查改操作(放在dao里)
IAccountDao 接口:(这里只用2个数据操作方法演示,其他方法都类似)
public interface IAccountDao {
//根据id查询
Account findAccountById(int id);
//更新数据
int updateAccount(Account account);
}
AccountDaoImpl 类,实现 IAccountDao 接口
public class AccountDaoImpl implements IAccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public Account findAccountById(int id) {
List<Account> accountList=jdbcTemplate.query("select * from t_account where id= ?",new BeanPropertyRowMapper<Account>(Account.class),5);
return accountList.get(0);
}
@Override
public int updateAccount(Account account) {
return jdbcTemplate.update("update t_account set money=? where id=?",account.getMoney(),account.getId());
}
}
启动类
public class AppNoteDemo {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
IAccountDao accountDao=ac.getBean("accountDao",IAccountDao.class);
Account account=accountDao.findAccountById(5);
System.out.println("根据id查询:"+account);
account.setMoney(1000);
System.out.println("更新数据的数量:"+accountDao.updateAccount(account));
}
}
改进写法
实际开发中会写多个dao实现类,这些类里都会有这些重复部分
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
JdbcDaoSupport 是Spring 提供的一个类,该类中定义了一个 JdbcTemplate 对象,我们可以直接获取使用,但是想创建该对象,需要为其提供一个数据源。
AccountDaoImpl 类修改一下
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
@Override
public Account findAccountById(int id) {
List<Account> accountList=getJdbcTemplate().query("select * from t_account where id= ?",new BeanPropertyRowMapper<Account>(Account.class),5);
return accountList.get(0);
}
@Override
public int updateAccount(Account account) {
return getJdbcTemplate().update("update t_account set money=? where id=?",account.getMoney(),account.getId());
}
}
bean.xml 修改
<?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: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/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">
<!--配置dao-->
<bean id="accountDao" class="com.rgb3.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置数据源-->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_note"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
</beans>
第一种方式在 Dao 类中定义 JdbcTemplate ,适用于 XML 配置方式和注解配置方式。第二种用继承的方式,只能用 XML 配置方式。
JdbcTemplate 事务控制(XML配置方式)
例子演示
还是引入上面一样的依赖,数据库表,Account 实体类、IAccountDao 接口和上面的一样。
AccountDaoImpl 类
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
@Override
public Account findAccountById(int id) {
List<Account> accountList=getJdbcTemplate().query("select * from t_account where id= ?",new BeanPropertyRowMapper<Account>(Account.class),id);
return accountList.get(0);
}
@Override
public int updateAccount(Account account) {
return getJdbcTemplate().update("update t_account set money=? where id=?",account.getMoney(),account.getId());
}
}
IAccountService 接口
public interface IAccountService {
void transfer(int sourceId, int targetId,float money);
}
AccountServiceImpl 类
//@Service("accountService")
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao){
this.accountDao=accountDao;
}
@Override
public void transfer(int sourceId, int targetId, float money) {
//1.根据名称查询两个账户
Account source = accountDao.findAccountById(sourceId);
Account target = accountDao.findAccountById(targetId);
//2.修改两个账户的金额
source.setMoney(source.getMoney()- money);//转出账户减钱
target.setMoney(target.getMoney()+ money);//转入账户加钱
// 3.更新两个账户
accountDao.updateAccount(source);
int i=1/0; //故意设置异常
accountDao.updateAccount(target);
}
}
bean.xml
<?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
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置service-->
<bean id="accountService" class="com.rgb3.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!--配置dao-->
<bean id="accountDao" class="com.rgb3.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_note"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--事务的配置-->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" read-only="false" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!--配置 AOP-->
<aop:config>
<!--配置切入点表达式-->
<aop:pointcut id="pt1" expression="execution(* com.rgb3.service.impl.*.*(..))"/>
<!--事务的建议与切入点表达式建立联系-->
<aop:advisor advice-ref="transactionAdvice" pointcut-ref="pt1"/>
</aop:config>
</beans>
配置事务时可以填的属性:
<tx:attributes>
<tx:method name="*" read-only="false" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
</tx:attributes>
read-only:是否是只读事务。默认 false,表示读写。
isolation:指定事务的隔离级别。默认值是使用数据库的默认隔离级别。
propagation:指定事务的传播行为。默认值 REQUIRED 表示一定会有事务,增删改操作可以选这个,值 SUPPORTS 查询方法可以选这个。
timeout:指定超时时间。默认值为:-1。永不超时。
rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚。产生其他异常,事务不回滚。 没有默认值,任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回滚。没有默认值,任何异常都回滚。
name="*":*为通配符,表示所有方法
name=“find*”:表示含有 find 方法名的方法(所以命名方法时注意规范)
启动类
public class AppNoteDemo {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
IAccountService accountService=ac.getBean("accountService",IAccountService.class);
accountService.transfer(5,3,100);
}
}
启动前
启动后报错,并且数据库里的数据没有任何改变,事务控制成功。
JdbcTemplate 事务控制(注解配置方式)
数据库表与上面的一样。
AccountDaoImpl 类
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Account findAccountById(int id) {
List<Account> accountList=jdbcTemplate.query("select * from t_account where id= ?",new BeanPropertyRowMapper<Account>(Account.class),id);
return accountList.get(0);
}
@Override
public int updateAccount(Account account) {
return jdbcTemplate.update("update t_account set money=? where id=?",account.getMoney(),account.getId());
}
}
AccountServiceImpl 类
@Service("accountService")
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
@Override
@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
public void transfer(int sourceId, int targetId, float money) {
//1.根据名称查询两个账户
Account source = accountDao.findAccountById(sourceId);
Account target = accountDao.findAccountById(targetId);
//2.修改两个账户的金额
source.setMoney(source.getMoney()- money);//转出账户减钱
target.setMoney(target.getMoney()+ money);//转入账户加钱
// 3.更新两个账户
accountDao.updateAccount(source);
//int i=1/0; //故意设置异常
accountDao.updateAccount(target);
}
}
@Transactional
配置事务的属性可以填在里面,该注解的属性和 XML配置方式中的属性含义一致。该注解可以出现在接口上,类上和方法上。
- 出现接口上,表示该接口的所有实现类都有事务支持。
- 出现在类上,表示类中所有方法有事务支持
- 出现在方法上,表示这个方法有事务支持。
- 以上三个位置的优先级:方法>类>接口。
JdbcConfig 配置类
//jdbc配置类
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;
@Bean(name ="jdbcTemplate")
public JdbcTemplate creaJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
//配置数据源
@Bean(name="dataSource")
public DataSource createDataSource(){
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
}
TransactionConfig 配置类
//事务配置类
public class TransactionConfig {
//创建事务管理器对象
@Bean(name = "transactionManager")
public PlatformTransactionManager createTransactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
注意:导入 DataSource 类时选择这个:
import javax.sql.DataSource;
jdbcConfig.properties 配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_note
jdbc.username=root
jdbc.password=root
MySpringConfiguration 配置类
@Configuration
@ComponentScan("com.rgb3") //开启扫描包位置
@Import({JdbcConfig.class,TransactionConfig.class})//导入其他配置类
@PropertySource("jdbcConfig.properties") //属性配置文件
@EnableTransactionManagement //开启事务控制管理
public class MySpringConfiguration {
}
启动前:
启动程序,成功转账
在AccountServiceImpl 类里故意设置异常测试一下
public void transfer(int sourceId, int targetId, float money) {
//1.根据名称查询两个账户
Account source = accountDao.findAccountById(sourceId);
Account target = accountDao.findAccountById(targetId);
//2.修改两个账户的金额
source.setMoney(source.getMoney()- money);//转出账户减钱
target.setMoney(target.getMoney()+ money);//转入账户加钱
// 3.更新两个账户
accountDao.updateAccount(source);
int i=1/0; //故意设置异常
accountDao.updateAccount(target);
}
启动出现异常,数据没有变化,成功事务控制。