本文主要介紹Spring中AOP註解,jdbc模板,事務的基本使用。 ...
一、AOP註解開發
-
導入jar包
aop聯盟包、 aspectJ實現包 、 spring-aop-xxx.jar 、 spring-aspect-xxx.jar
-
導入約束
aop約束
-
托管擴展類和被擴展類
<!-- 要做AOP, 一定要托管擴展類和被擴展類 --> <bean id="us" class="com.pri.service.impl.UserServiceImpl"></bean> <bean id="logger" class="com.pri.util.Logger"></bean>
也可以使用註解來托管這兩個類 。 @Component
-
在擴展出來的功能類身上和方法上打註解
@Aspect //這個註解,是和aop有關,用於表示該類是一個功能擴展類,專門用來做增強的。 public class Logger { @Before("execution(* com.pri.service.impl.*.*(..))") public void log(){ System.out.println("輸出日誌了~~~!"); } }
二、 Jdbc模板
-
為什麼spring也要提供jdbc模板呢?
spring是屬於service層的框架, 它所處的位置比較尷尬,並且它想讓自己更受程式員的喜愛。 除了能夠做好自己的核心
IOC
&AOP
, 它還對前後兩個框架都提供了支持。 spring其實對dao層的大部分技術有提供模板支持 。
1. jdbc模板的入門
public void testDemo(){ //數據源,連資料庫。 連什麼類型的數據, 哪個庫, 賬號 & 密碼 DriverManagerDataSource dataSource =new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///stus"); dataSource.setUsername("root"); dataSource.setPassword("root"); //1. 創建jdbc模板 JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); String sql = "insert into t_user values(null , ? , ? )"; //2. 執行添加操作,其實它的用法和以前的queryrunner一樣。 jdbcTemplate.update(sql , "66666","zhangsan"); }
2. crud操作
-
insert
String sql = "insert into t_user values (null , ? , ?)";
jdbcTemplate.update(sql, "heima24","6666");
-
delete
@Test public void testDelete(){ //3. 執行添加操作 String sql = "delete from t_user where uid = ?"; jdbcTemplate.update(sql, 11); }
-
udpate
@Test public void testUpdate(){ //3. 執行添加操作 String sql = "update t_user set password=? where uid=?"; jdbcTemplate.update(sql, "888",11); }
-
findCount
@Test public void testFindCount(){ //3. 執行添加操作 String sql = "select count(*) from t_user"; int count = jdbcTemplate.queryForObject(sql, Integer.class); System.out.println("count="+count); }
-
findObject
@Test public void testFindObject(){ //3. 執行添加操作 String sql = "select * from t_user where uid = ?"; User user = jdbcTemplate.queryForObject(sql,new MyRowMapper() , 10); System.out.println("user="+user); }
-
findList
@Test public void testFindList(){ //3. 執行添加操作 String sql = "select * from t_user "; List<User> user = jdbcTemplate.query(sql, new MyRowMapper()); System.out.println("user="+user); }
查詢要求我們手動封裝class MyRowMapper implements RowMapper<User>{
@Override public User mapRow(ResultSet rs, int arg1) throws SQLException { System.out.println("arg1=="+arg1); User user = new User(); user.setUid(rs.getInt("uid")); user.setUsername(rs.getString("username")); user.setPassword(rs.getString("password")); return user; } }
3. jdbc模板的註入寫法
該小節演練的是三層結構的所有屬性註入寫法。(不寫action | servlet)
1. 分析
以下代碼其實是位於dao層的
@Override public void save(User user) { // 數據源,連資料庫。 連什麼類型的數據, 哪個庫, 賬號 & 密碼 DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///stus"); dataSource.setUsername("root"); dataSource.setPassword("root"); // 1. 創建jdbc模板 JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); String sql = "insert into t_user values(null , ? , ? )"; // 2. 執行添加操作,其實它的用法和以前的queryrunner一樣。 jdbcTemplate.update(sql, user.getPassword(), user.getUsername()); }
這套代碼很多,而且它只是一個dao的操作代碼而已。 如果我們還有別的dao方法。也要執行資料庫操作,那麼必然也得寫出來這樣的代碼。 那麼就存在冗餘代碼了。
解決方案:
-
以前的解決方法:
工具類 | 靜態代碼塊
-
現在學了spring了,我想用spring的手法來簡化dao的這一套代碼。
分析以上代碼,其實它就是做了兩個工作。
a. 創建了jdbc模板的對象,並且完成了屬性的賦值工作。
b. 創建了dataSource 的對象,並且完成了屬性的賦值工作。
-
spring也可以完成這個工作, 創建對象,其實就是`IOC`的體現, 對屬性的賦值工作其實就是`DI`的體現
•~~~xml <bean id="jdbctemplate" class=""> <property name="dataSource" ref="dataSource"> <bean id="dataSource" class=""> <property name="..." value=""> <property name="..." value=""> <property name="..." value=""> <property name="..." value=""> •~~~
2. 基本實現
-
service
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("調用了UserServiceImpl的save方法~~!");
userDao.save();
}
}
-
dao
public class UserDaoImpl implements UserDao { private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public void save() { System.out.println("調用了UserDaoImpl的save方法~~"); //3. 執行添加操作 String sql = "insert into t_user values (null , ? , ?)"; jdbcTemplate.update(sql, "heima242","0000"); } }
-
xml
<bean id="us" class="com.pri.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <bean id="userDao" class="com.pri.dao.impl.UserDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <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:///stus"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean>
3. 整合c3p0連接池
-
導入jar包
c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar
-
在xml中配置
沒有使用c3p0連接池的配置 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///user"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean> 使用了c3p0的配置 <!-- 使用c3p0連接池 --><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql:///user"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
4. 使用jdbc.properties記錄資料庫連接信息
為了方便修改資料庫連接信息,通常在src下使用properties文件來記錄他們。主要是方便修改
-
在src下新建一個properties文件,內容如下 名:jdbc.properties
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql:///user
user=root
password=root
-
在xml裡面導入jdbc.properties ,並且在dataSource裡面引用
a. 導入context約束 b. 導入jdbc.properties <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 使用c3p0連接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${driverClass}"></property> <property name="jdbcUrl" value="${jdbcUrl}"></property> <property name="user" value="${user}"></property> <property name="password" value="${password}"></property> </bean>
三、 事務管理
1. 事務的回顧
-
什麼事務,事務有什麼用?
事務是用於包裝一組操作的。當這一組操作裡面的所有步驟都執行成功,那麼這一組操作就算成功(事務的提交),如果有哪一個步驟執行失敗,那麼這一組操作就以失敗告終。(事務的回顧)
-
事務的特性
原子性
、一致性
、隔離性
、持久性
-
如果不考慮隔離級別,那麼事務併發會出現以下問題
-
讀的問題
臟讀 : 讀到了另一個事務還沒有提交的數據
不可重覆讀 : 讀到了另一個事務已經提交的數據 (針對的是update的數據)
虛讀| 幻讀 : 讀到了另一個事務已經提交的數據(針對的是insert的數據)
解決方案: 設置隔離級別
讀未提交
讀已提交 Oracle
可重覆讀 MySql
序列化|串列化
-
寫的問題
丟失更新: 最後操作的事務,不管是提交還是回滾,都會讓前一個是的數據丟失掉。
解決方案:
悲觀鎖 : 還沒開始就認為一定會丟失更新。
每一個事務在執行操作之前,先進行一次查詢。 並且查詢的語句裡面帶有關鍵字
for update
樂觀鎖 : 認為不會出現丟失更新,
要求程式員手動控制,給表額外添加一個欄位 如: version。
2. Spring對事務的管理辦法
-
事務應該寫在哪一層? 為什麼spring也要插手管理事務?
事務應該寫在Service層。 因為service表示的業務,一組業務可能會執行多個到的方法。所以在Service層聲明事務更好一點。 而且spring正好是Service層的解決方案。
-
Spring針對事務的管理API
Spring 可以管理事務, 但是真正完成操作的技術,spring可不管。 dao層使用的技術可能有很多。
jdbc
、hibernate
、mybatis
. 但是這幾個框架,他們開啟事務的辦法,可能不一樣。 spring為了統一管理事務,聲明瞭一套規範出來,並且對底下的使用頻率比較到的技術,都給出了具體的實現。
管理事務的規範是 : PlatformTransactionManager
jdbc | mybatis : DataSourceTransactionManager
hibernate : HibernateTransactionManager
spring對dao層使用什麼技術,它不管,它統一規定,要操作事務,必須使用管理員!!!
3. spring對事務支持的寫法
提供了三種寫法
編程式事務
、聲明式事務(xml & 註解)
1. 編程式事務
純靠寫代碼來完成事務配置
@Test public void test(){ //事務的管理員是用來管理事務的,包含了打開事務、 提交事務、 回滾事務 final DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///stus"); dataSource.setUsername("root"); dataSource.setPassword("root"); DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); //1. 創建事務的模板對象。 TransactionTemplate transactionTemplate = new TransactionTemplate(); transactionTemplate.setTransactionManager(transactionManager); //事務事務的模板對象 transactionTemplate.execute( new TransactionCallback<Object>() { //在事務裡面執行這個方法。 @Override public Object doInTransaction(TransactionStatus arg0) { //添加操作 JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); String sql = "insert into t_user values ( null , ? , ?)"; jdbcTemplate.update(sql, "123","王五"); int a = 1 / 0 ; jdbcTemplate.update(sql, "6666","趙六"); return null; } }); }
2. 聲明式事務(xml)
xml方式配置事務,其實就是使用AOP的思想,在方法執行前,開啟事務,方法執行後,提交事務(回滾事務)
<!-- 以下屬於事務的配置 如果要用事務了,那麼事務的管理員是誰啊。 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:advice id="advice01" transaction-manager="transactionManager"> <tx:attributes> <!-- 對前面的表達式找到的那一堆方法,進行過濾配置,表示誰才能用事務管理, 如果是* ,表示前面找到的那一堆都用事務管理 --> <tx:method name="save*"/> <tx:method name="update*"/> <tx:method name="delete*"/> </tx:attributes> </tx:advice> <!-- 以上配置,到目前為止,只是說明瞭,如果要開啟事務,管理員是誰。 但是缺少了一個東西。 就是哪一個方法到底要用事務呀? --> <aop:config> <aop:pointcut expression="execution(* com.pri.service.impl.*.*(..))" id="aa"/> <aop:advisor advice-ref="advice01" pointcut-ref="aa"/> </aop:config>
3. 聲明式事務(註解)
使用註解的方式來開啟事務
-
xml配置
1. 在xml中 聲明註解事務的管理員 <!-- 以下屬於事務的配置 如果要用事務了,那麼事務的管理員是誰啊。 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 指定註解事務使用的管理員是誰 --> <tx:annotation-driven transaction-manager="transactionManager"/>
-
代碼配置
2. 在業務邏輯類上或者方法上打註解
類上的註解,表示類中的所有方法都用事務, 如果在方法上打註解,表示只有這個方法才會用事務
@Transactional public class UserServiceImpl implements UserService { } public class UserServiceImpl implements UserService { @Transactional @Override public void save() {} }