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" />