工程結構:
注意:src目錄下的applicationContext.xml是單元測試用的,作爲web工程的話就使用WEB-INF下的applicationContext.xml。
1、下載jar包
這裏需要下載很多spring的jar包,推薦去官網下載,很多人會說官網只提供maven和gradle下載,沒有jar下載,那是你沒有認真解讀spring官網,同意是專注於web開源項目,spring的官網就有很多地方值得我們學習,別管英語水平高低,不行就藉助翻譯工具,扯遠了。這裏介紹一下如何從spring官網下載spring框架jar包。
- 百度spring官網或者直接進入spring框架的網頁http://projects.spring.io/spring-framework/
- 在maven依賴包的上面有這麼一段話“All avaible features and modules are described in the Modules section of the reference documentation. Their maven/gradle coordinates are also described there.”
- 有2個鏈接地址,點進去第一個鏈接地址“the Modules section of the reference documentation“裏面都是spring的介紹,
- 往下拉,找到”Distribution Zip Files“就找到了壓縮包下載地址http://repo.spring.io/release/org/springframework/spring,
- 進去就可以下載各個版本的spring框架jar包了。這裏我們使用4.2.2
這個工程中用到這些spring的jar包:
2、創建web工程
1)把下載的spring的zip壓縮包解壓,把以下jar包複製到工程的lib目錄下。
2) WEB-INF下創建web.xml
作爲web工程,容器啓動時會先加載web.xml,我們就把spring的加載放到web.xml裏:
註冊spring的中央控制器listener—ContextLoaderListener;
註冊spring配置文件的位置;
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <!-- 註冊用於加載spring框架的中央控制器 listener ContextLoaderListener 原理:由於該監聽器實現自ServletContextListener,因此它監聽的是整個web容器(web.xml) --> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <!-- contextConfigLocation是spring框架中指定spring配置文件 找到spring配置文件後,創建一個ioc對象加載、解析配置文件,解析的同時實例化所有bean對象 監聽器將其實例化的WebApplicationContext保存在web容器中,即保存在ServletContext中 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
3) WEB-INF下創建spring的配置文件 applicationContext.xml
主要是裝配bean,依賴注入和數據庫連接池配置、事務等。
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 註冊properties類 --> <bean id="jdbcConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations" value="classpath:jdbc.properties"/> </bean> <!-- spring jdbc --> <!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClassName" value="${jdbc.driverClassName}"></property> </bean> --> <!-- DBCP BasicDataSource提供了close()方法關閉數據源,所以必須設定destroy-method=”close”屬性,以便Spring容器關閉時,數據源能夠正常關閉--> <!-- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClassName" value="${jdbc.driverClassName}"></property> </bean> --> <!-- c3p0 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <!-- 注意name和dbcp不同 --> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClass" value="${jdbc.driverClassName}"></property> <!--連接池中保留的最大連接數。默認值: 15 --> <property name="maxPoolSize" value="20"/> <!-- 連接池中保留的最小連接數,默認爲:3--> <property name="minPoolSize" value="2"/> <!-- 初始化連接池中的連接數,取值應在minPoolSize與maxPoolSize之間,默認爲3--> <property name="initialPoolSize" value="2"/> <!--最大空閒時間,60秒內未使用則連接被丟棄。若爲0則永不丟棄。默認值: 0 --> <property name="maxIdleTime" value="60"></property> <!-- 當連接池連接耗盡時,客戶端調用getConnection()後等待獲取新連接的時間,超時後將拋出SQLException,如設爲0則無限期等待。單位毫秒。默認: 0 --> <property name="checkoutTimeout" value="3000"/> <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。默認值: 3 --> <property name="acquireIncrement" value="2"/> <!--定義在從數據庫獲取新連接失敗後重復嘗試的次數。默認值: 30 ;小於等於0表示無限次--> <property name="acquireRetryAttempts" value="0"/> <!--重新嘗試的時間間隔,默認爲:1000毫秒--> <property name="acquireRetryDelay" value="1000" /> <!--關閉連接時,是否提交未提交的事務,默認爲false,即關閉連接,回滾未提交的事務 --> <property name="autoCommitOnClose" value="false"></property> <!--c3p0將建一張名爲Test的空表,並使用其自帶的查詢語句進行測試。如果定義了這個參數那麼屬性preferredTestQuery將被忽略。你不能在這張Test表上進行任何操作,它將只供c3p0測試使用。默認值: null --> <property name="automaticTestTable" value="c3p0testtable"></property> <!--如果爲false,則獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常,但是數據源仍有效保留,並在下次調用getConnection()的時候繼續嘗試獲取連接。如果設爲true,那麼在嘗試獲取連接失敗後該數據源將申明已斷開並永久關閉。默認: false--> <property name="breakAfterAcquireFailure" value="false"></property> <!--每60秒檢查所有連接池中的空閒連接。默認值: 0,不檢查 --> <property name="idleConnectionTestPeriod" value="60"></property> <!--c3p0全局的PreparedStatements緩存的大小。如果maxStatements與maxStatementsPerConnection均爲0,則緩存不生效,只要有一個不爲0,則語句的緩存就能生效。如果默認值: 0--> <property name="maxStatements" value="100"></property> <!--maxStatementsPerConnection定義了連接池內單個連接所擁有的最大緩存statements數。默認值: 0 --> <property name="maxStatementsPerConnection" value="0"></property> </bean> <!-- 事務配置在service層,dao層的異常要拋出來事務才生效,可以通過配置切面或者用註解的方法使用事務 --> <!-- 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 使用事務有2種方式, 1、可以通過配置通知+切面的方式 可以配置一類方法使用事務 開發時不用考慮事務 2、 使用註解@Transactional 可以精準的配置要使用事務的方法 開發是需要考慮事務 --> <!-- 配置通知 --> <tx:advice id="serviceAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="del*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="add*" propagation="REQUIRED"/> <tx:method name="find*" propagation="REQUIRED"/> <tx:method name="get*" propagation="REQUIRED"/> <tx:method name="query*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置切面 --> <aop:config> <!-- 切入點 --> <aop:pointcut id="servicePointcut" expression="execution(* com.chen.service.*.*(..))" /> <aop:advisor advice-ref="serviceAdvice" pointcut-ref="servicePointcut"/> </aop:config> <!--採用@Transactional註解方式使用事務 --><!-- <tx:annotation-driven transaction-manager="transactionManager" /> --> <bean id="userDao" class="com.chen.dao.UserDao"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <bean id="userService" class="com.chen.service.UserService"> <property name="userDao" ref="userDao"></property> </bean> </beans>
4)spring配置文件詳解
- 裝配bean:
首先配置bean userDao
<bean id="userDao" class="com.chen.dao.UserDao"></bean>
再配置bean userService
<bean id="userService" class="com.chen.service.UserService"> <property name="userDao" ref="userDao"></property> </bean>
property元素就是把userDao注入給userService,當然在代碼中也要注入,在UserService中
private UserDao userDao;
public void setUserDao(UserDao userDao)
{
this.userDao = userDao;
}這樣在UserService中就可以使用userDao了。
- 數據庫連接
配置數據庫連接有幾種方式,其實就是配置dataSource,然後注入給jdbcTemplate
a、spring jdbc方式,
這用方式每次都創建連接,效率低資源消耗大:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClassName" value="${jdbc.driverClassName}"></property> </bean>
b、dbcp 連接池
<!-- BasicDataSource提供了close()方法關閉數據源,所以必須設定destroy-method=”close”屬性,以便Spring容器關閉時,數據源能夠正常關閉--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClassName" value="${jdbc.driverClassName}"></property> </bean>
c、c3p0 連接池
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <!-- 注意name和dbcp不同 --> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClass" value="${jdbc.driverClassName}"></property> <!--連接池中保留的最大連接數。默認值: 15 --> <property name="maxPoolSize" value="20"/> <!-- 連接池中保留的最小連接數,默認爲:3--> <property name="minPoolSize" value="2"/> <!-- 初始化連接池中的連接數,取值應在minPoolSize與maxPoolSize之間,默認爲3--> <property name="initialPoolSize" value="2"/> <!--最大空閒時間,60秒內未使用則連接被丟棄。若爲0則永不丟棄。默認值: 0 --> <property name="maxIdleTime" value="60"></property> <!-- 當連接池連接耗盡時,客戶端調用getConnection()後等待獲取新連接的時間,超時後將拋出SQLException,如設爲0則無限期等待。單位毫秒。默認: 0 --> <property name="checkoutTimeout" value="3000"/> <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。默認值: 3 --> <property name="acquireIncrement" value="2"/> <!--定義在從數據庫獲取新連接失敗後重復嘗試的次數。默認值: 30 ;小於等於0表示無限次--> <property name="acquireRetryAttempts" value="0"/> <!--重新嘗試的時間間隔,默認爲:1000毫秒--> <property name="acquireRetryDelay" value="1000" /> <!--關閉連接時,是否提交未提交的事務,默認爲false,即關閉連接,回滾未提交的事務 --> <property name="autoCommitOnClose" value="false"></property> <!--c3p0將建一張名爲Test的空表,並使用其自帶的查詢語句進行測試。如果定義了這個參數那麼屬性preferredTestQuery將被忽略。你不能在這張Test表上進行任何操作,它將只供c3p0測試使用。默認值: null --> <property name="automaticTestTable" value="c3p0testtable"></property> <!--如果爲false,則獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常,但是數據源仍有效保留,並在下次調用getConnection()的時候繼續嘗試獲取連接。如果設爲true,那麼在嘗試獲取連接失敗後該數據源將申明已斷開並永久關閉。默認: false--> <property name="breakAfterAcquireFailure" value="false"></property> <!--每60秒檢查所有連接池中的空閒連接。默認值: 0,不檢查 --> <property name="idleConnectionTestPeriod" value="60"></property> <!--c3p0全局的PreparedStatements緩存的大小。如果maxStatements與maxStatementsPerConnection均爲0,則緩存不生效,只要有一個不爲0,則語句的緩存就能生效。如果默認值: 0--> <property name="maxStatements" value="100"></property> <!--maxStatementsPerConnection定義了連接池內單個連接所擁有的最大緩存statements數。默認值: 0 --> <property name="maxStatementsPerConnection" value="0"></property> </bean>
- 配置事務
個人覺得采用註解方式更好用
<!-- 事務配置在service層,dao層的異常要拋出來事務才生效,可以通過配置切面或者用註解的方法使用事務 --> <!-- 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 使用事務有2種方式, 1、可以通過配置通知+切面的方式 可以配置一類方法使用事務 開發時不用考慮事務 2、 使用註解@Transactional 可以精準的配置要使用事務的方法 開發時需要考慮事務 --> <!-- 配置通知 --> <tx:advice id="serviceAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="del*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="add*" propagation="REQUIRED"/> <tx:method name="find*" propagation="REQUIRED"/> <tx:method name="get*" propagation="REQUIRED"/> <tx:method name="query*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置切面 --> <aop:config> <!-- 切入點 --> <aop:pointcut id="servicePointcut" expression="execution(* com.chen.service.*.*(..))" /> <aop:advisor advice-ref="serviceAdvice" pointcut-ref="servicePointcut"/> </aop:config> <!--採用@Transactional註解方式使用事務 --> <tx:annotation-driven transaction-manager="transactionManager" />
java代碼
userDao
package com.chen.dao; import org.springframework.jdbc.core.JdbcTemplate; public class UserDao { private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public Map queryMap(String projectId) { Map map = null; try { String sql = "select * from t_fw_um_project where id = "+projectId; String [] args = {projectId}; map = jdbcTemplate.queryForMap(sql); } catch(Exception e) { e.printStackTrace(); } return map; } public List queryList() { List list = null; try { String sql = "select * from t_fw_um_project"; list = jdbcTemplate.queryForList(sql); } catch(Exception e) { e.printStackTrace(); } return list; } public List<Map<String,Object>> queryChildren(String permissionId) { List<Map<String, Object>> list = null; try { String sql = "select * from t_fw_um_permission t where t.project_id = '111' and t.parent_id in ( " + permissionId +" )"; list = jdbcTemplate.queryForList(sql); } catch(Exception e) { e.printStackTrace(); } return list; } //刪權限依賴 public int deleteDependPermissions(String permissionIds) { int result = 0; try { String sql = "delete from t_fw_um_permission_depend where c_id in ( " + permissionIds +" ) or d_id in ( " + permissionIds +" )"; result = jdbcTemplate.update(sql); } catch(Exception e) { e.printStackTrace(); throw e; } return result; } //刪權限角色關聯表 public int deleteRolePermissions(String permissionIds) { int result = 0; try { String sql = "delete from t_fw_um_role_permission_map where PERMISSION_ID in ( " + permissionIds +" )"; result = jdbcTemplate.update(sql); } catch(Exception e) { e.printStackTrace(); throw e; } return result; } //刪權限 public int deletePermissions(String permissionIds) { int result = 0; try { String sql = "delete from t_fw_um_permission where id in ( " + permissionIds +" )"; int i = 1/0; result = jdbcTemplate.update(sql); } catch(Exception e) { e.printStackTrace(); throw e; } return result; } }
UserService
package com.chen.service; import java.util.List; import java.util.Map; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.transaction.annotation.Transactional; import com.chen.dao.UserDao; public class UserService { //注入dao 否則無法使用事務 private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } //@Test public void createUser() { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); UserDao userDao = (UserDao) ctx.getBean("userDao"); //System.out.println(userDao.queryMap("666")); System.out.println(userDao.queryChildren("3622d013-dadd-4d63-9930-4b7859f2e0d9")); } //@Test//這裏可以使用註解,或者配置的方式使用事務 //@Transactional public void deletePermission() { //ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //UserDao userDao = (UserDao) ctx.getBean("userDao"); String permissionId = "'09073677-64e0-4932-85f1-65fe3137e108'"; StringBuilder delPermissions = new StringBuilder(); delPermissions.append(permissionId).append(","); List<Map<String,Object>> list = userDao.queryChildren(permissionId); while(list.size() > 0) { //每次查出來的children,作爲下次查詢的父類 StringBuilder queryPermissions = new StringBuilder(); //把權限id取出來 for(Map<String,Object> m : list) { delPermissions.append("'").append(m.get("id")).append("',"); queryPermissions.append("'").append(m.get("id")).append("',"); } String queryPs = queryPermissions.toString(); queryPs = queryPs.substring(0, queryPs.length()-1); list = userDao.queryChildren(queryPs); } String delPermission = delPermissions.toString(); delPermission = delPermission.substring(0, delPermission.length()-1); p(delPermission); //p("刪依賴關係="+userDao.deleteDependPermissions(delPermission)); //p("刪角色權限關聯="+userDao.deleteRolePermissions(delPermission)); //p("刪權限="+userDao.deletePermissions(delPermission)); p("刪依賴關係="+userDao.deleteDependPermissions(delPermission)); p("刪權限="+userDao.deletePermissions(delPermission)); } public void p(Object o) { System.out.println(o); } }
在src下創建jdbc.properties數據源文件
jdbc.driverClassName = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://localhost:3306/數據庫名 jdbc.username = root jdbc.password = 000000