spring 框架学习-4
spring 框架学习安排
目录
1. 第四天
1. spring中的JdbcTemplate
1.JdbcTemplate的CRUD基本使用
- JdbcTemplate的作用:与数据库进行交互,进行CRUD操作
- 最简单的demo
public class JDBCTemplateDemo1 {
public static void main(String[] args) {
// 使用spring自带的数据源
DriverManagerDataSource dmds = new DriverManagerDataSource();
dmds.setDriverClassName("com.mysql.jdbc.Driver");
dmds.setUsername("root");
dmds.setPassword("809080");
dmds.setUrl("jdbc:mysql://localhost:3306/eesy");
JdbcTemplate template = new JdbcTemplate(dmds);
template.execute("insert into account(name,money)values('yyy',4000)");
}
}
- 发现问题,有多个setXXX的出现,可以使用IOC来注入,添加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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"></property>
</bean>
<!--配置datasource-->
<bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
<property name="username" value="root"></property>
<property name="password" value="809080"></property>
</bean>
</beans>
重新测试上述demo
/**
* Created by liuzeyu on 2020/4/25.
* 使用spring IOC
*/
public class JDBCTemplateDemo2 {
public static void main(String[] args) {
//使用IOC
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
JdbcTemplate jdbcTemplate = (JdbcTemplate) ac.getBean("jdbcTemplate");
jdbcTemplate.execute("delete from account where name like '%y%'");
}
}
- 进行CRUD操作
/**
* Created by liuzeyu on 2020/4/25.
* JdbcTemplate 的 GRUD操作
*/
public class JDBCTemplateDemo3 {
public static void main(String[] args) {
//使用IOC
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
JdbcTemplate jdbcTemplate = (JdbcTemplate) ac.getBean("jdbcTemplate");
//1. 对数据库插入一条数据
//jdbcTemplate.update("insert into account (name,money)values(?,?)","liuzeyu",34444);
//2. 修改一条数据
//jdbcTemplate.update("update account set money=8888 where name=?","liuzeyu");
//3. 删除一条数据
//jdbcTemplate.update("delete from account where name like ?","%liuz%");
//4. 查询所有
//List<Account> accounts = jdbcTemplate.query("select * from account where money > ?", new subMapper(), 100);
// List<Account> accounts = jdbcTemplate.query("select * from account where money > ?",
// new BeanPropertyRowMapper<Account>(Account.class), 100);
// for (Account account : accounts) {
// System.out.println(account);
// }
//5. 查询一个
// List<Account> query = jdbcTemplate.query("select * from account where money > ?",
// new BeanPropertyRowMapper<Account>(Account.class), 1000);
// System.out.println(query.isEmpty() ? "查询为空" :query.get(0));
//6. 查询一个
Integer count = jdbcTemplate.queryForObject("select count(*) from account where money >?", Integer.class, 0);
System.out.println(count);
}
}
class subMapper implements RowMapper{
public Account mapRow(ResultSet resultSet, int i) throws SQLException {
Account account = new Account();
account.setId(resultSet.getInt("id"));
account.setMoney(resultSet.getFloat("money"));
account.setName(resultSet.getString("name"));
return account;
}
}
其中
所以我们通常使用spring自带的
new BeanPropertyRowMapper<T>(T.class)
2.JdbcTemplate的DAO层使用
- dao层代码
public interface IAccountDao {
//根据id查询
public Account findAccountById(Integer id);
//根据name查询
public Account findAccountByName(String name);
}
public class AccountDaoImpl implements IAccountDao {
private JdbcTemplate jdbcTemplate = null;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Account findAccountById(Integer id) {
List<Account> accounts = jdbcTemplate.query("select * from account where id=?",
new BeanPropertyRowMapper<Account>(Account.class), id);
Account account = accounts.isEmpty() ?null:accounts.get(0);
return account;
}
public Account findAccountByName(String name) {
List<Account> accounts = jdbcTemplate.query("select * from account where name =?",
new BeanPropertyRowMapper<Account>(Account.class), name);
if(accounts.isEmpty()){
return null;
}
if(accounts.size() > 1){
throw new RuntimeException("查询结果不唯一");
}
return accounts.get(0);
}
}
- bean.xml添加jdbcTemplate注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置accountDao-->
<bean id="accountDao" class="com.liuzeyu.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!--配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"></property>
</bean>
<!--配置datasource-->
<bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
<property name="username" value="root"></property>
<property name="password" value="809080"></property>
</bean>
</beans>
- 测试方法
public class JDBCTemplateDemo4 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountDao accountDao = ac.getBean("accountDao", IAccountDao.class);
Account byId = accountDao.findAccountById(1);
//System.out.println(byId);
Account byName = accountDao.findAccountByName("ccc");
//System.out.println(byName);
}
}
3. Spring提供JdbcDaoSupport的支持
2中Dao层存在的问题:如果有多个dao,则下面代码将重复出现多次
如何解决这一个问题呢?我们可以定义一个类,并把重复的代码抽取出来
public class DaoSupport {
public JdbcTemplate jdbcTemplate = null;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate = new JdbcTemplate();
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void setDataSource(DataSource source){
if(jdbcTemplate == null)
jdbcTemplate = new JdbcTemplate(source);
}
public JdbcTemplate createJdbcTemplate(DataSource source) {
return new JdbcTemplate(source);
}
}
那如何获取这个datasource呢?可以注入
因为dao的实现类继承了该方法,那它一定继承了该方法的setDataSource方法,于是可以注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置accountDao-->
<bean id="accountDao" class="com.liuzeyu.dao.impl.AccountDaoImpl">
<!--<property name="jdbcTemplate" ref="jdbcTemplate"></property>-->
<property name="dataSource" ref="ds"></property>
</bean>
<!--配置JdbcTemplate已经不需要了-->
<!--<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">-->
<!--<property name="dataSource" ref="ds"></property>-->
<!--</bean>-->
<!--配置datasource-->
<bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
<property name="username" value="root"></property>
<property name="password" value="809080"></property>
</bean>
</beans>
dao层实现类的使用可以直接super.jdbcTemplate得到
当然。spring这么强大,已经为我们提供了一个DaoSupport 来处理多处编写jdbcTemplate的问题。也是可以以上面注入的相同方式得到jdbcTemplate
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao
2. 作业:spring中基于AOP的事务控制
github链接
说明:注解配置和老师的演示结果有所不同,后期又必要深入研究。
4. spring中的事务控制
1. 基于XML的声明式事务控制
-
准备工程
- dao层代码
/** * 账户的持久层接口 */ public interface IAccountDao { /** * 根据Id查询账户 * @param accountId * @return */ Account findAccountById(Integer accountId); /** * 根据名称查询账户 * @param accountName * @return */ Account findAccountByName(String accountName); /** * 更新账户 * @param account */ void updateAccount(Account account); }
/** * 账户的持久层实现类 */ public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao { public Account findAccountById(Integer accountId) { List<Account> accounts = super.getJdbcTemplate().query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId); return accounts.isEmpty()?null:accounts.get(0); } public Account findAccountByName(String accountName) { List<Account> accounts = super.getJdbcTemplate().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); } public void updateAccount(Account account) { super.getJdbcTemplate().update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId()); } }
- service层代码
package com.liuzeyu.service; import com.liuzeyu.domain.Account; /** * 账户的业务层接口 */ public interface IAccountService { /** * 根据id查询账户信息 * @param accountId * @return */ Account findAccountById(Integer accountId); /** * 转账 * @param sourceName 转成账户名称 * @param targetName 转入账户名称 * @param money 转账金额 */ void transfer(String sourceName, String targetName, Float money); }
/** * 账户的业务层实现类 * * 事务控制应该都是在业务层 */ public class AccountServiceImpl implements IAccountService{ private IAccountDao accountDao; public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } public Account findAccountById(Integer accountId) { return accountDao.findAccountById(accountId); } 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); } }
- 实体类
package com.liuzeyu.domain; import java.io.Serializable; /** * 账户的实体类 */ public class Account implements Serializable { private Integer id; private String name; private Float money; public Integer getId() { return id; } public void setId(Integer 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 + '}'; } }
- pom.xml & bean.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.liuzeyu12a</groupId> <artifactId>day04_spring_txXML</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <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.0.2.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> </dependencies> </project>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--配置accountDao--> <bean id="accountDao" class="com.liuzeyu.dao.impl.AccountDaoImpl"> <property name="dataSource" ref="ds"></property> </bean> <bean id="accountService" class="com.liuzeyu.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> <!--配置datasource--> <bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property> <property name="username" value="root"></property> <property name="password" value="809080"></property> </bean> </beans>
- 测试函数
/** * 使用Junit单元测试:测试我们的配置 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:bean.xml") public class AccountServiceTest { @Autowired private IAccountService as; @Test public void testTransfer(){ as.transfer("aaa","bbb",100f); } }
-
开始配置基于XML的声明式事务控制
- 需要导入aspect的依赖
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency>
- 在原有的bean.xml基础上配置事务
<!--spring中基于XML的声明式事务控制(我们不用再自己写事务管理器类和方法) 1.配置事务管理器 2.配置事务管理器通知(此时我们需要引入事务管理器约束+tx名称空间和约束)同时也需要aop 使用tx:advice配置事务管理器通知 属性: id:给事务通知起一个唯一标识 transaction:给事务通知引用一个事务管理器 3.配置aop的通用切入点表达式 --> <!--1.配置事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="ds"></property> </bean> <!--2.配置事务管理器通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!--2.1配置事务的属性 isolation="" :用于指定事务的隔离级别。默认为default,表示使用数据库的隔离级别 rollback-for="":表示用于指定一个异常,当产生该异常时,回滚事务,产生其它异常时,事务不回滚。没有默认值表示任何异常都回滚。 no-rollback-for="":表示用于指定一个异常,当产生该异常时,不回滚事务,产生其它异常时,事务回滚,没有默认值表示任何异常都回滚。 propagation="":用于指定事务的传播行为。默认值时REQUIRED,用于增删改的选择。查询方法使用SUPPORT read-only="":用于指定事务是否只读,只有查询方法才能涉及为true,默认为false,表示读写 timeout="":用于指定事务的超时时间,默认为-1表示永不超时,如果指定了时间,以秒为单位 --> <tx:attributes> <!--增删改--> <tx:method name="transfer" propagation="REQUIRED" read-only="false"/> <!--查询方法--> <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method> </tx:attributes> </tx:advice> <!--3.配置aop--> <aop:config> <!--3.1配置aop的通用切入点表达式--> <aop:pointcut id="pt" expression="execution(* com.liuzeyu.service.impl.*.*(..))"></aop:pointcut> <!--3.2建立切入点表达式和事务通知的对应关系--> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor> </aop:config>
2. 基于注解的声明式事务控制(基于上个项目)
- 将dao层和service层的实习类加入IOC容器中
@Service("accountService")
@Transactional
public class AccountServiceImpl implements IAccountService{...}
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
@Autowired
private JdbcTemplate template = null;
由于使用了注解配置,因此要想获取template的值只能从bean.xml重新将JdbcTemplate 添加到IOC容器中,然后使用@Autowired注入
- 修改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: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/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">
<!--创建IOC容器要扫描的包-->
<context:component-scan base-package="com.liuzeyu"></context:component-scan>
<!--导入jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"></property>
</bean>
<!--配置datasource-->
<bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
<property name="username" value="root"></property>
<property name="password" value="809080"></property>
</bean>
<!--spring中基于注解事务控制(我们不用再自己写事务管理器类和方法)
1.配置事务管理器
2.开启spring对注解事务的支持
-->
<!--1.配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="ds"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
- 对业务层引入事务控制,添加注解@Transactional
@Service("accountService")
@Transactional
public class AccountServiceImpl implements IAccountService{...}
@Transactional表示属性去默认值,也可以加到方法上,但如果遇到不同的方法,我们必须指定不同的属性,于是需要配置多次,这是比xml配置繁琐的。
2. 基于纯注解的声明式事务控制
- 无需修改dao层和service层的代码,添加配置类
@Configuration
@ComponentScan(basePackages = "com.liuzeyu") //扫描包
@EnableTransactionManagement //开启事务管理
@Import(value = {JdbcConfiguration.class,TransactionConfiguration.class}) //子配置类
@PropertySource("jdbc.properties") //引入外部资源文件
public class SpringConfiguration {
}
- 将上面的bean.xml配置文件使用注解进行配置
资源文件:
jdbc.url=jdbc:mysql://localhost:3306/eesy
jdbc.username=root
jdbc.password=809080
jdbc.driver=com.mysql.jdbc.Driver
JdbcConfiguration 子配置类
/**
* Created by liuzeyu on 2020/4/26.
*/
public class JdbcConfiguration {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.driver}")
private String driver;
@Bean("jdbcTemplate")//将JdbcTemplate对象加入到容器中
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean("dataSource") //将DriverManagerDataSource 对象加入到容器中
public DataSource createDataSouruce(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(url);
dataSource.setPassword(password);
dataSource.setUsername(username);
dataSource.setDriverClassName(driver);
return dataSource;
}
}
事务管理类配置
public class TransactionConfiguration {
@Bean("transactionManager") //将PlatformTransactionManager对象加入到容器中
public PlatformTransactionManager createTransactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}