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);
}
}