本文主要介紹Spring中, 1 Spring JDBC 2 使用註解方式管理事務的傳播行為 3 採用XML 方式配置事務 4 SH 整合 5 SSH 整合 一、Spring JDBC 1) 導包 , 使用myeclipse2014, 添加與持久化相關的包 2) 引入名稱空間等 3) 配置數據源 4 ...
本文主要介紹Spring中,
1 Spring JDBC
2 使用註解方式管理事務的傳播行為
3 採用XML 方式配置事務
4 SH 整合
5 SSH 整合
一、Spring JDBC
1) 導包 , 使用myeclipse2014, 添加與持久化相關的包
2) 引入名稱空間等
3) 配置數據源
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><!-- 單實例bean -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
<property name="initialSize" value="10"/> <!-- 連接池啟動時的初始值 -->
<property name="maxActive" valu3) e="300"/> <!-- 連接池的最大值 -->
<property name="maxIdle" value="5"/> <!-- 最大空閑值.當經過一個高峰時間後,連接池可以慢慢將已經用不到的連接慢慢釋放一部分,一直減少到maxIdle為止 -->
<property name="minIdle" value="3"/> <!-- 最小空閑值.當空閑的連接數少於閥值時,連接池就會預申請去一些連接,以免洪峰來時來不及申請 -->
</bean>
4) dao層
@Repository("userDaoImpl") public class UserDaoImpl { private JdbcTemplate jdbcTemplate; //用於進行資料庫操作的模板,線程安全的 @Resource public void setDataSource(DataSource dataSource){ this.jdbcTemplate=new JdbcTemplate(dataSource); } //添加 public int addUser(UserInfo user){ String sql="insert into userInfo (userName,password,note) values(?,?,?)" ; Object[] paramList={user.getUserName(),user.getPassword(),user.getNote()}; //也可以不用定義數組,直接這樣寫 :return jdbcTemplate.update(sql, user.getUserId(),user.getUserName(),user.getPassword(),user.getNote()); return jdbcTemplate.update(sql, paramList); } //刪除 public int delUser(int id){ String sql="delete from userInfo where id=?"; Object[] paramList={id}; return jdbcTemplate.update(sql, paramList); } //修改 public int update(UserInfo user){ String sql="update userInfo set userName=?,password=?, note=? where id=?"; Object[] paramList={user.getUserName(),user.getPassword(),user.getNote(),user.getId()}; return jdbcTemplate.update(sql, paramList); } //查詢 public UserInfo getUserById(int id){ String sql="select * from userInfo where id=?"; //註意:如果查詢結果多於一條,將報異常 Object[] paramList={id}; Object user=jdbcTemplate.queryForObject(sql, paramList, new BeanPropertyRowMapper(UserInfo.class)); return (UserInfo)user; } //查詢出列表 public List<UserInfo>getUserList(){ String sql="select * from userInfo"; return (List<UserInfo>)jdbcTemplate.query(sql,new BeanPropertyRowMapper(UserInfo.class)); } //查詢出單個Int值 public int getUserCount(){ String sql="select count(*) from userInfo"; return jdbcTemplate.queryForInt(sql); } //只查詢一個欄位 public String getUserName(int id){ String sql="select userName from userInfo where id= "+id; return (String)jdbcTemplate.queryForObject(sql, String.class); //因為要查詢的欄位是String型的,所以在這裡用 String.class } //返回map public Map getUserMapData(int id){ String sql="select * from userInfo where id="+id; return jdbcTemplate.queryForMap(sql); } //添加一個用戶,返回其ID public void addUser2(final UserInfo user){ //註意,這裡必須用 final jdbcTemplate.execute(new ConnectionCallback<Object>() { //org.springframework.jdbc.core.包下的 ConnectionCallback @Override public Object doInConnection(Connection conn) throws SQLException,DataAccessException { String sql="insert into userInfo (userName,password,note) values(?,?,?)" ; PreparedStatement stm=conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); stm.setString(2, user.getUserName()); stm.setString(3, user.getPassword()); stm.setString(4, user.getNote()); stm.executeUpdate(); ResultSet rs=stm.getGeneratedKeys(); if(rs.next()){ System.out.println("新添加的用戶的主鍵是:"+rs.getInt(1)); } return null; } }); } //拿到自增主鍵的另一個方法,來自幫助文檔 public void ttt(){ KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update( new PreparedStatementCreator() { public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { PreparedStatement ps = connection.prepareStatement("insert into my_test (name) values(?)", new String[] {"id"}); ps.setString(1, "張三"); return ps; } }, keyHolder); keyHolder.getKey(); //這就是拿到的自增主鍵 } }
5) 測試用例
public class UserDaoImplTest { private static ClassPathXmlApplicationContext ctx; private static UserDaoImpl dao; @BeforeClass public static void setUpBeforeClass() throws Exception { ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); dao=(UserDaoImpl)ctx.getBean("userDaoImpl"); } @Before public void setUp() throws Exception { } @Test public void testAddUser() { UserInfo user=new UserInfo(); user.setUserName("趙明明"); user.setPassword("忘了"); user.setNote("又想起來了"); dao.addUser(user); System.out.println("-----------嘻嘻---------------"); } @Test public void testDelUser() { int result=dao.delUser(196611); System.out.println(result); } @Test public void testUpdate() { UserInfo user=new UserInfo(); user.setId(196610); user.setUserName("費無極"); user.setPassword("大費費"); user.setNote("中小型費費"); int result=dao.update(user); System.out.println(result); } @Test public void testGetUserById() { UserInfo user=dao.getUserById(196609); System.out.println(user.getNote()); } @Test public void testGetUserList() { List<UserInfo>userList=dao.getUserList(); for(UserInfo u:userList){ System.out.println(u.getUserName()); } } @Test public void testGetUserCount() { int result=dao.getUserCount(); System.out.println(result); } @Test public void testGetUserName() { String userName=dao.getUserName(491553); System.out.println(userName); } @Test public void testGetUserMapData() { Map userMap=dao.getUserMapData(2); Set<Map.Entry>entrySet=userMap.entrySet(); Iterator <Map.Entry>it=entrySet.iterator(); while(it.hasNext()){ Map.Entry item=it.next(); System.out.println(item.getKey()+"---"+item.getValue()); } } @Test public void testAddUser2() { UserInfo user=new UserInfo(); user.setUserName("奧八馬"); user.setPassword("小狒狒"); user.setNote("超級小狒狒"); dao.addUser2(user); } @Test public void testTtt() { }
二、使用註解方式管理事務的傳播行為
//刪除 public int delUser(int id){ String sql="delete from userInfo where id=?"; Object[] paramList={id}; int result= jdbcTemplate.update(sql, paramList); int a=90/0; //如果程式在這裡出現異常,會不會回滾,預設情況下,不回滾 return result; }
在spring中開啟事務管理
1)要加入相應的名稱空間
xmlns:tx="http://www.springframework.org/schema/tx" ... http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
2) 配置事務管理器
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 使用註解的方式管理事務(不使用註解,不用加這句) -->
<tx:annotation-driven transaction-manager="txManager"/>
3) 在要進行事務管理的類上,用註解聲明
@Transactional @Repository("userDaoImpl") public class UserDaoImpl { public int delUser(int id){ String sql="delete from userInfo where id=?"; Object[] paramList={id}; int result= jdbcTemplate.update(sql, paramList); int a=90/0; //如果程式在這裡出現異常,會不會回滾,由於聲明瞭事務,所以會回滾 return result; } ... } //對這個方法進行細粒度的控制 @Transactional(noRollbackFor=RuntimeException.class) public void delAllUser(){ String sql="delete from userInfo "; jdbcTemplate.execute(sql); throw new RuntimeException(發生了運行時異常); }
這段代碼要刪除所有用戶,但運行時拋出了異常,所以事務會回滾,數據不會被刪除,可以更改這種行為,在方法前加上註解。
@Transactional(noRollbackFor=RuntimeException.class) 表示出了異常也不回滾。
有的方法比如查詢方法,不需要事務,可以在該方法上指定。
@Transactional(propagation=Propagation.NOT_SUPPORTED) //加上以後,這個方法就不再支持事務了 public List<UserInfo>getUserList(){ String sql="select * from userInfo"; return (List<UserInfo>)jdbcTemplate.query(sql,new BeanPropertyRowMapper(UserInfo.class)); }
事務的傳播行為
----REQUIRED:業務方法需要在一個事務中運行。如果方法運行時,已經處在一個事務中,那麼加入到該事務,否則為自己創建一個新的事務。
----NOT_SUPPORTED:聲明方法不需要事務。如果方法沒有關聯到一個事務,容器不會為它開啟事務。如果方法在一個事務中被調用,該事務會被掛起,在方法調用結束後,原先的事務便會恢復執行。
----REQUIRESNEW:(requiresnew) 屬性表明不管是否存在事務,業務方法總會為自己發起一個新的事務。如果方法已經運行在一個事務中,則原有事務會被掛起,新的事務會被創建,直到方法執行結束,新事務才算結束,原先的事務才會恢復執行。
----MANDATORY (mandatory,強制的,命令的,受委托的):該屬性指定業務方法只能在一個已經存在的事務中執行,業務方法不能發起自己的事務。 如果業務方法在沒有事務的環境下調用,容器就會拋出例外。
----SUPPORTS:這一事務屬性表明,如果業務方法在某個事務範圍內被調用,則方法成為該事務的一部分。如果業務方法在事務範圍外被調用,則方法在沒有事務的環境下執行。
----Never:指定業務方法絕對不能在事務範圍內執行。如果業務方法在某個事務中執行,容器會拋出例外,只有業務方法沒有關聯到任何事務,才能正常執行。
----NESTED:nested (窩,嵌套)如果一個活動的事務存在,則運行在一個嵌套的事務中。如果沒有活動事務, 則按REQUIRED屬性執行.它使用了一個單獨的事務, 這個事務擁有多個可以回滾的保存點。
內部事務的回滾不會對外部事務造成影響。它只對DataSourceTransactionManager事務管理器起效。
REQUIRED 是預設的 ,比如
//這裡什麼都不寫,相當於寫上 @Transactional(propagation=Propagation.REQUIRED) public int delUser(int id){ String sql="delete from userInfo where id=?"; Object[] paramList={id}; int result= jdbcTemplate.update(sql, paramList); int a=90/0; //如果程式在這裡出現異常,會不會回滾 return result; public void testTx(UserInfo user){ jdbcTemplate.update("delete from userInfo where id=1"); addUser(user); jdbcTemplate.update("delete from userInfo where id=2"); //int x=0/0; }
說明:
對於這個例子來說,沒有指明事務的傳播行為,所以這是預設的 @Transactional(propagation=Propagation.REQUIRED)。
對於 addUser 來說,如果如果方法運行時,已經處在一個事務中,則它將加入這個事務。所以,上面的操作,將會成為一個整體。
三、採用XML 方式配置事務
<aop:config> <aop:pointcut id="myTxPointCut" expression="execution(* cat.dao.UserDaoImpl_other.*(..))" /> <aop:advisor advice-ref="txAdvisor" pointcut-ref="myTxPointCut" /> </aop:config> <tx:advice id="txAdvisor" transaction-manager="txManager" > <tx:attributes> <tx:method name="get*" read-only="true" propagation="NOT_SUPPORTED" /> <tx:method name="*" /> </tx:attributes> </tx:advice>
用XML配置的方式,就不用再寫
<tx:annotation-driven transaction-manager="txManager"/>
@Repository("userDaoImpl_other") public class UserDaoImpl_other { .... public void testTx(UserInfo user){ jdbcTemplate.update("delete from userInfo where id=5"); addUser(user); jdbcTemplate.update("delete from userInfo where id=6"); int x=0/0; } public static void main(String[] args) { ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); UserDaoImpl_other dao=(UserDaoImpl_other)ctx.getBean("userDaoImpl_other"); UserInfo user=new UserInfo(); user.setUserName("9999999999999"); user.setPassword("小狒狒"); user.setNote("超級小狒狒"); dao.testTx(user); } }
四、SH 整合
1) 導入名稱空間
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
2) 打開掃描
<context:component-scan base-package="cat" />
3) 配置 DataSource //hibernate用的
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/production?useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="root"/> <property name="initialSize" value="10"/> 連接池啟動時的初始值 <property name="maxActive" value="500"/> 連接池的最大值 <property name="maxIdle" value="2"/> 最大空閑值.當經過一個高峰時間後,連接池可以慢慢將已經用不到的連接慢慢釋放一部分,一直減少到maxIdle為止 <property name="minIdle" value="3"/> 最小空閑值.當空閑的連接數少於閥值時,連接池就會預申請去一些連接,以免洪峰來時來不及申請 </bean>
4) 配置SessionFactory //hibernate用的
/* sessionFactory 建議做成單例 LocalSessionFactoryBean 除了可建一個 sessionFactory 對象出來,把它做成單例,還專門用於集成 Hibernate ,用於做一些額外的工作,比如接管hibernate的事務 */ <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" > <property name="dataSource"> <ref bean="dataSource" /> </property> <property name="mappingResources"> <list> <value>cat/beans/UserInfo.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.hbm2ddl.auto=update hibernate.show_sql=true hibernate.format_sql=true //配置二級緩存用的 // hibernate.cache.use_second_level_cache=true // hibernate.cache.use_query_cache=false //是否使用查詢緩存 因為它的效率比較差,所以不用 // hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider //指定的緩存產品 要導入 lib\optional\ejcacje -1-1-3/jar </value> </property> </bean>
5) 配置事務管理器 txManager
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven transaction-manager="txManager"/> 如果要用註解方式管理事務,要加這個配置
五、SSH 整合
再和 struts2 進行整合
1) 在web.xml 中 對Spring容器進行初始化
//指定spring 的配置文件,預設從web根目錄尋找配置文件,可以通過string 提供的classpath:首碼指定從類路徑下尋找 <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:beans.xml</param-value> 如果配置多個配置文件,可以用逗號隔開 </context-param> // 對spring 容器進行實例化 ,實例化後,這個實例會放在applicateion 範圍--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
2) 在web.xml中添加struts2應用
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>杠*</url-pattern> </filter-mapping>
3) 在類路徑下添加 sruts.xml 文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <constant name="struts.objectFactory" value="spring" /> //聲明要用spring來創建 action 類 <package name="p_user" namespace="" extends="struts-default"> <action name="userAction_name" class="userAction"> <result name="success">/main.jsp</result> </action> </package> </struts>
4) Action 類
@Controller public class UserAction { @Resource(name="userDaoImpl") private IUserDao dao; public String execute(){ UserInfo user=new UserInfo(); user.setUserName("SSH整合成功"); dao.addUser(user); return "success"; } }
5)介面
public interface IUserDao { void addUser(UserInfo user) ; void delUser(int id) ; UserInfo getUserById(int id); List<UserInfo>getUserList(); void updateUser(UserInfo user); void deleteUser(UserInfo user); }
6)實現類
@Transactional @Repository public class UserDaoImpl implements IUserDao{ @Resource //這個註解預設是按名稱註入 private SessionFactory sessionFactory; public void addUser(UserInfo user) { //Session s=sessionFactory.openSession(); //它得到的是spring容器管理的session Session s=sessionFactory.getCurrentSession(); s.save(user); } public void delUser(int id) { Session s=sessionFactory.getCurrentSession(); UserInfo user=(UserInfo)s.get(UserInfo.class, id); s.delete(user); //先查一下,再刪除 } @Transactional(propagation=Propagation.NOT_SUPPORTED) public UserInfo getUserById(int id) { Session s=sessionFactory.getCurrentSession(); return (UserInfo)s.get(UserInfo.class, id); } @Transactional(propagation=Propagation.NOT_SUPPORTED) public List<UserInfo> getUserList() { Session s=sessionFactory.getCurrentSession(); return s.createQuery("from UserInfo").list(); } public void updateUser(UserInfo user) { Session s=sessionFactory.getCurrentSession(); s.saveOrUpdate(user); } public void deleteUser(UserInfo user) { Session s=sessionFactory.getCurrentSession(); s.delete(user); } }
附 如何知道一個類在哪個jar包中
ctl + shift + t
附 關於被Spring 管理的 dao 使用介面訪問的問題
Spring的文檔中這麼寫的:Spring AOP部分使用JDK動態代理或者CGLIB來為目標對象創建代理,如果被代理的目標對象實現了至少一個介面,則會使用JDK動態代理。所有該目標類型實現的介面都將被代理。若該目標對象沒有實現任何介面,則創建一個CGLIB代理。使用beanNameAutoProxyCreator來進行事務代理的話,他的proxyTargetClass這個屬性設置為false(預設是false),即使用JDK動態代理,如果你的service類沒有實現介面的話,就會報類型轉換錯誤。
解決辦法有:
1、給service類添加一個介面iService,讓service類實現它,則創建代理類時使用JDK動態代理就不會出現問題
2、設置beanNameAutoProxyCreator的proxyTargetClass屬性為true,意思是強制使用CGLIB代理,前提是你已經將CGLIB包加入到項目中。
其實,就是 在配置文件中配置:
<aop:aspectj-autoproxy proxy-target-class="true"/>
但要註意:
需要導入 cglib-nodep-2.2.jar (它可以從myeclipse 自動生成的 Spring 項目中獲得),還要導入 aspectj 相關的幾個jar包。