1 使用數據庫
1.1 配置數據源
1.1.1 JNDI 數據源
< bean id = "dataSource"
class = "org.springframework.jndi.JndiObjectFactoryBean" scope = "singleton" >
< property name = "jndiName" value = "/jdbc/RantzDatasource" />
< property name = "resourceRef" value = "true" />
</ bean >
resourceRef :當爲 true 的時候, jndiName 會被添加 java:comp/env/
Spring2.0 裏使用 jee 命名空間裏的配置
< jee:jndi-lookup id = "dataSource"
jndi-name = "/jdbc/RantzDatasource"
resource-ref = "true" />
1.1.2 基於 JDBC 驅動的數據源
< bean id = "dataSource"
class = "org.springframework.jdbc.datasource.DriverManagerDataSource" >
< property name = "driverClassName"
value = " oracle.jdbc.driver.OracleDriver " />
< property name = "url"
value = " jdbc:oracle:thin:@192.168.1.1:1521:test " />
< property name = "username" value = "zrl" />
< property name = "password" value = "" />
</ bean >
1.1.3 使用數據源連接池
類似 JDBC 驅動,使用 org.apache.commons.dbcp.BasicDataSource 類驅動
1.2 使用 JDBC
1.2.1 使用 JDBC 模板
編寫基於 JDBC 的 DAO 涉及配置 JDBC 模板 Bean 、把它裝配到 DAO 類、然後使用這個模板訪問數據庫,這個過程要配置 3 個 Bean :一個數據源、一個模板、一個 DAO
JdbcTemplate :最基本的 JDBC 模板,利用 JDBC 和簡單的索引參數對數據庫的簡單訪問
NamedParameterJdbcTemplate :能夠在執行查詢時把值綁定到 SQL 裏的命名參數,而不是使用索引參數
SimpleJdbcTemplate :選用 java5 的特性,來簡化 JDBC 模板的使用
1.2.1.1 JdbcTemplate
1) XML 配置
< bean id = "jdbcTemplate"
class = "org.springframework.jdbc.core.JdbcTemplate" >
< property name = "dataSource" ref = "dataSource" />
</ bean >
2) 把 JdbcTemplate 裝配到 DAO
public class JdbcRantDao implements RantDao {
...
private JdbcTemplate jdbcTemplate ;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this . jdbcTemplate =jdbcTemplate;
}
}
3) 設置 JdbcRantDao 的 jdbcTemplate 屬性
< bean id = "rantDao" class = "com.roadrantz.dao.jdbc.JdbcRantDao" >
< property name = "jdbcTemplate" ref = "jdbcTemplate" />
</ bean >
4) 使用保存
private static final String MOTORIST_INSERT = "insert into motorist (id, email, password, firstName, lastName) "
+ "values (null, ?,?,?,?)" ;
public void saveMotorist(Motorist motorist) {
jdbcTemplate .update(
MOTORIST_INSERT ,
new Object[] { motorist.getEmail(),
motorist.getPassword(), motorist.getFirstName(),
motorist.getLastName() });
}
5) 使用查詢
private static final String MOTORIST_SELECT = "select id, email, password, firstName, lastName from motorist" ;
private static final String MOTORIST_BY_ID_SELECT = MOTORIST_SELECT
+ " where id=?" ;
public Motorist getMotoristById ( long id) {
List matches = jdbcTemplate .query( MOTORIST_BY_ID_SELECT ,
new Object[] { Long.valueOf (id) }, new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum)
throws SQLException,
DataAccessException {
Motorist motorist = new Motorist();
motorist.setId(rs.getInt(1));
motorist.setEmail(rs.getString(2));
motorist.setPassword(rs.getString(3));
motorist.setFirstName(rs.getString(4));
motorist.setLastName(rs.getString(5));
return motorist;
}
});
return matches.size() > 0 ? (Motorist) matches.get(0) : null ;
}
1.2.1.2 NamedParameterJdbcTemplate
使用方法及步驟同 jdbcTemplate
命名參數:
private static final String MOTORIST_INSERT = "insert into motorist (id, email, password, firstName, lastName) "
+ "values (null, :email, :password, :firstName, :lastName)" ;
使用保存
public void saveMotorist (Motorist motorist) {
Map<String, String> parameters = new HashMap<String, String>();
parameters.put( "email" , motorist.getEmail());
parameters.put( "password" , motorist.getPassword());
parameters.put( "firstName" , motorist.getFirstName());
parameters.put( "lastName" , motorist.getLastName());
jdbcTemplate .update( MOTORIST_INSERT , parameters);
}
1.2.1.3 SimpleJdbcTemplate
使用方法及步驟同 jdbcTemplate
查詢
public Motorist getMotoristById ( long id) {
List<Motorist> matches = jdbcTemplate .query(
MOTORIST_BY_ID_SELECT ,
new ParameterizedRowMapper<Motorist>() {
public Motorist mapRow(ResultSet rs, int rowNum)
throws SQLException {
Motorist motorist = new Motorist();
motorist.setId(rs.getInt(1));
motorist.setEmail(rs.getString(2));
motorist.setPassword(rs.getString(3));
motorist.setFirstName(rs.getString(4));
motorist.setLastName(rs.getString(5));
return motorist;
}
}, id);
return matches.size() > 0 ? matches.get(0) : null ;
}
1.2.2 使用 Spring 對 JDBC 的 DAO 支持類
DAO 類繼承 JdbcDaoSupport
public class JdbcRantDao extends JdbcDaoSupport implements RantDao
只需配置 jdbcTemplate 或 dataSource
< bean id = "rantDao" class = "com.roadrantz.dao.jdbc.JdbcRantDao" >
< property name = "dataSource" ref = "dataSource" />
</ bean >
< bean id = "rantDao" class = "com.roadrantz.dao.jdbc.JdbcRantDao" >
< property name = "jdbcTemplate " ref = "jdbcTemplate " />
</ bean >
使用 getJdbcTemplate().update
NamedParameterJdbcDaoSupport 、 SimpleJdbcDaoSupport 的使用方法類似
1.3 在 Spring 裏集成 Hibernate
1.3.1 使用 Hibernate 模板
1) 配置 HibernateTemplate
< bean id = "hibernateTemplate"
class = "org .springframework .orm .hibernate3.HibernateTemplate" >
< property name = "sessionFactory" ref = "sessionFactory" />
</ bean >
2) 配置 sessionFactory
使用典型的 Hibernate 映射文件
< bean id = "sessionFactory"
class = "org .springframework .orm .hibernate3.LocalSessionFactoryBean" >
< property name = "dataSource" ref = "dataSource" />
< property name = "mappingResources" >
< list >
< value > com /roadrantz /domain/Rant.hbm .xml </ value >
< value > com /roadrantz /domain/Motorist.hbm .xml </ value >
< value > com /roadrantz /domain/Vehicle.hbm .xml </ value >
</ list >
</ property >
< property name = "hibernateProperties" >
< props >
< prop key = "hibernate .dialect" > ${hibernate .dialect} </ prop >
</ props >
</ property >
</ bean >
Java5 也可以使用被註解的域對象(這裏不做說明)
3) 通過 Hibernate 模板訪問數據
public class HibernateRantDao implements RantDao {
...
private Hibernate Template hibernate Template;
public void setTemplate( Hibernate Template template) {
this .hibernate Template =template;
}
}
4) Spring 裏配置 DAO
< bean id = "rantDao" class = "com.roadrantz.dao.hibernate.HibernateRantDao" >
< property name = "hibernateTemplate" ref = "hibernateTemplate" />
</ bean >
5) 使用
public Vehicle findVehicleByPlate(String state, String plateNumber) {
List results = getHibernateTemplate()
.find( "from " + VEHICLE + " where state = ? and plateNumber = ?" ,
new Object[] { state, plateNumber });
if (results.size() > 0) {
return (Vehicle) results.get(0);
}
return null ;
}
public Motorist getDriverById (Integer id) {
return (Motorist) getHibernateTemplate().load(Motorist. class , id);
}
1.3.2 建立基於 Hibernate 的 DAO
1) 繼承 HibernateDaoSupport
public class HibernateRantDao extends HibernateDaoSupport implements RantDao
2) 數據訪問方法使用 getHibernateTemplate()
3) 修改 spring 配置的 Dao
< bean id = "rantDao" class = "com.roadrantz.dao.hibernate.HibernateRantDao" >
< property name = "sessionFactory" ref = "sessionFactory" />
</ bean >
1.3.3 使用 Hibernate3 上下文會話
HibernateTemplate 的缺點是具有一定的侵入性。使用上下文把 DAO 實現與 Spring 解耦,缺點是它們拋出 Hibernate 特有的異常
1) 定義 session
public class HibernateContextualSessionsRantDao implements RantDao {
...
private SessionFactory sessionFactory ;
public void setSessionFactory(SessionFactory sessionFactory) {
this . sessionFactory = sessionFactory;
}
}
2) Spring 裏配置的 DAO 同上
3) 訪問
public void saveRant (Rant rant) {
sessionFactory .getCurrentSession() .saveOrUpdate(rant);
}
1.4 緩存
Spring Modules 會提供一個代理來攔截方法並把結果保存到緩存,它並沒有提供一個實際的緩存解決方案,而是要依賴於第三方的緩存方案。這裏選擇 EHCache 。
1.4.1 配置緩存方案
< beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns:ehcache = "http://www.springmodules.org/schema/ehcache"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springmodules.org/schema/ehcache
http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd" >
< ehcache:config
configLocation = "classpath:ehcache.xml" />
< ehcache:annotations >
< ehcache:caching id = "rantzCacheModel" cacheName = "rantzCache" />
</ ehcache:annotations >
</ beans >
EHCache.xml 文件內容
< ehcache >
< defaultCache
maxElementsInMemory = "500"
eternal = "true"
overflowToDisk = "false"
memoryStoreEvictionPolicy = "LFU" />
< cache name = "rantzCache"
maxElementsInMemory = "500"
eternal = "true"
overflowToDisk = "false"
memoryStoreEvictionPolicy = "LFU" />
</ ehcache >
<defaultCache> 是必須有的, <cache> 定義了另一個緩存
memoryStoreEvictionPolicy 當達到最大時的驅逐策略:最近使用 (LRU) 、先入先出 (FIFO) 、較入使用 (LFU)
1.4.2 緩存的代理 Bean
< ehcache:proxy id = "rantDao"
refId = "rantDaoTarget" >
< ehcache:caching
methodName = "getRantsForDay"
cacheName = "rantzCache" />
< echache:flushing
methodName = "saveRant"
cacheName = "rantzCache"
when = "before" />
</ ehcache:proxy >
< ehcache:caching> 元素聲明哪個方法要被攔截、其返回要保存到哪個緩存
< echache:flushing> 元素聲明瞭會清空緩存的方法
1.4.3 註解驅動的緩存
@Cacheable :聲明一個方法的返回值應該被緩存
@CacheFlush :聲明一個方法是清空緩存的觸發器
@CacheFlush (modelId= "rantzCacheModel" )
public void saveRant(Rant rant);
@Cacheable (modelId= "rantzCacheModel" )
public List<Rant> getAllRants();
在 xml 中配置
< ehcache:annotations >
< ehcache:caching id = "rantzCacheModel" cacheName = "rantzCache" />
< ehcache:caching id = "rantzFlushModel" cacheName = "rantzCache" when = "before" />
</ ehcache:annotations >
1.5 事務管理
ACID :原子性( Atomic )、一致性( Consistent )、隔離性( Isolated )、持久性( Durable )
1.5.1 事務管理器
1.5.1.1 JDBC 事務
< bean id = "transactionManager"
class = "org.springframework.jdbc.datasource.DataSourceTransactionManager " >
< property name = "dataSource" ref = "dataSource" />
</ bean >
1.5.1.2 Hibernate 事務
< bean id = "transactionManager"
class = "org.springframework.orm.hibernate3.HibernateTransactionManager " >
< property name = "sessionFactory" ref = "sessionFactory" />
</ bean >
1.5.2 在 Spring 中編寫事物
略
1.5.3 聲明式事務
public class RantServiceImpl implements RantService {
public void addRant(Rant rant) {
rant.setPostedDate( new Date());
Vehicle rantVehicle = rant.getVehicle();
Vehicle existingVehicle = rantDao .findVehicleByPlate(rantVehicle
.getState(), rantVehicle.getPlateNumber());
if (existingVehicle != null ) {
rant.setVehicle(existingVehicle);
} else {
rantDao .saveVehicle(rantVehicle);
}
rantDao .saveRant(rant);
}
}
1.5.3.1 代理事務
< bean id = "rantService" class = "org.springframework.transaction.interceptor.TransactionProxyFactoryBean" >
< property name = "target" ref = "rantServiceTarget" /> // 裝配事務目標
< property name = "proxyInterfaces" value = "com.roadrantz.service.RantService" /> // 指定代理接口
< property name = "transactionManager" ref = "transactionManager" /> // 裝配事務管理器
< property name = "transactionAttributes" > // 裝配事務規則、邊界
< props >
< prop key = "add*" > PROPAGATION_REQUIRED </ prop >
< prop key = "*" > PROPAGATION_SUPPORTS,readOnly </ prop >
</ props >
</ property >
</ bean >
PROPAGATION_REQUIRED: 表示此方法必須在一個事務中運行
PROPAGATION_SUPPORTS :表示當前方法不需要事務性上下文。
1.5.3.2 創建一個事務代理模板
父 Bean
< bean id = "txProxyTemplate" class = "org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract=”true” >
< property name = "transactionManager" ref = "transactionManager" /> // 裝配事務管理器
< property name = "transactionAttributes" > // 裝配事務規則、邊界
< props >
< prop key = "add*" > PROPAGATION_REQUIRED </ prop >
< prop key = "*" > PROPAGATION_SUPPORTS,readOnly </ prop >
</ props >
</ property >
</ bean >
子 Bean
< bean id = "rantService" parent="txProxyTemplate" >
< property name = "target" ref = "rantServiceTarget" />
< property name = "proxyInterfaces" value = "com.roadrantz.service.RantService" />
</ bean >
1.5.3.3 在 Spring2.0 裏聲明事務
增加 tx 名稱空間
xmlns:tx = "http://www.springframework.org/schema/tx"
xsi:schemaLocation = "...
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd" >
配置通知
< tx:advice id = "txAdvice" transaction-manager = "transactionManager" >
< tx:attributes >
< tx:method name = "add*" propagation = "REQUIRED" />
< tx:method name = "*" propagation = "REQUIRED" read-only = "true" />
</ tx:attributes >
</ tx:advice >
通知器
< aop:config >
< aop:advisor advice-ref = "txAdvice"
pointcut = "execution(* *..RantService.*(..))" />
</ aop:config >
表示通知 RantService 接口的所有方法
1.5.3.4 註釋驅動事務
@Transactional (propagation = Propagation. SUPPORTS , readOnly = true )
public interface RantService {
@Transactional (propagation = Propagation. REQUIRED )
public void addRant(Rant rant);
...
}
只需在 XML 上加上以下配置代碼
< tx:annotation-driven transaction-manager = "transactionManager" />