Spring对持久层的支持
1.简述
① JDBC,② O/R Mapping(Hibernate,TopLink等)
Spring对持久层支持采用的策略:
1、Spring对持久层“不发明重复的轮子”,即没有重新实现新的持久层方案,对现有持久层方案做封装,更利于使用。
2、采用DAO模式
3、提供了大量的模板类来简化编程(HibernateDaoSupport,JdbcTemplate等)
4、重新设计了一套完善的异常体系结构
① 类型丰富,细化异常类型
② 全都是运行时异常(RuntimeException)
2.spring的数据访问模板
CciTemplate JacCci连接,适用于WebLogic,WebSphere平台
JdbcTemplate JDBC连接
NameParameterJdbcTemplate 支持参数命名的JDBC连接
SimpleJdbcTemplate JDBC连接
HibernateTemplate 支持Hibernate2,3的会话
JpaTempate Java存储JPA实体管理
SqlMapClientTemplate iBatis
<!-- 配置sessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
</bean>
<!--配置hibernate的模板-->
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sesscionFactory" ref="sessionFactory"></property>
</bean>
数据访问模板,并不是Spring的全部,每个模板还提供了一些有用的方法,让我们不必创建明确的回调实现。
同时,Spring提供了DAO的支持类,用于派生出我们自己的DAO类。
如:如果程序DAO继承了JdbcDaoSupport支持类,那么我们只需要调用getJdbcTemplate访问就可以获取一个JdbcTemplate模板。
Spring提供的支持类如下:
CciDaoSupport
JdbcDaoSupport
NameParameterJdbcDaoSupport
HibernateDaoSupport
SqlMapClientDaoSupport
spring对jdbc的支持
1、配置数据源
方式一:采用Spring内置的数据源,Spring内置实现 DriverManagerDataSource
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/senssic</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>qiyu0126</value>
</property>
</bean>
方式二:采用开源数据库产品如DBCP
DBCP提供的BasicDataSource
需要下载commons-dbcpjar包和 commons-pool.jarhttp://archive.apache.org/dist/commons/dbcp/binaries/commons-dbcp-1.2.1.zip
http://jarfiles.pandaidea.com/c/commons-pool/commons-pool-1.5.jar.zip
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/senssic</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>qiyu0126</value>
</property>
<!-- 连接池启动时的初始值 -->
<property name="initialSize" value="1"/>
<!-- 连接池的最大值 -->
<property name="maxActive" value="500"/>
<!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
<property name="maxIdle" value="2"/>
<!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
<property name="minIdle" value="1"/>
</bean>
也可以写一个propertis文件来配置数据源然后用占位的方式映射到xml中
<context:property-placeholder location=“jdbc.properties”/> <!--指定properties文件,classees路径下-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
<!-- 连接池启动时的初始值 -->
<property name="initialSize" value="${initialSize}"/>
<property name="maxActive" value="${maxActive}"/>
<property name="maxIdle" value="${maxIdle}"/>
<property name="minIdle" value="${minIdle}"/>
</bean>
方式三:采用开源数据库产品c3po连接池
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-
method="close" lazy-init="false">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/senssic"/>
<property name="user" value="root" />
<property name="password" value="qiyu0126" />
<property name="testConnectionOnCheckin" value="true" />
<property name="automaticTestTable" value="TestTable" />
<property name="idleConnectionTestPeriod" value="18" />
<property name="maxIdleTime" value="250" />
<property name="testConnectionOnCheckout" value="true" />
</bean>
方式四: 直接使用容器提供的数据源(如Tomcat,Weblogic,Sun Application Server)
JNDI数据源:(mysql5,tomcat5.5)
step1:
在server.xml中:
<Resource name="jdbc/mydatasource" auth="Container" description="DB Connection"
type="javax.sql.DataSource" username="root" password="qiyu0126"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/senssic" maxActive="5" />
step2:
在context.xml中(conf\context.xml):
<ResourceLink name="jdbc/mydatasource" global="jdbc/mydatasource" type="javax.sql.DataSourcer"/>
step3:
在beans-config.xml:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:jdbc/mydatasource</value>
</property>
</bean>
1、使用Spring的jdbcTemplate进一步简化JDBC操作
在xml中配置 jdbcTemplate<?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:aop="http://www.springframework.org/schema/aop"
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/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<context:annotation-config></context:annotation-config>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/senssic</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>qiyu0126</value>
</property>
<!-- 连接池启动时的初始值 -->
<property name="initialSize" value="1"/>
<!-- 连接池的最大值 -->
<property name="maxActive" value="500"/>
<!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
<property name="maxIdle" value="2"/>
<!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
<property name="minIdle" value="1"/>
</bean>
<!-- 配置spring的jdbcTemplate -->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate" abstract="false"
lazy-init="false" autowire="default" >
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<!-- 配置spring的事务管理 (DataSourceTransactionManager提供对JDBC,iBatis的支持,DataSource,当然还有JtaTransactionManager 对应JTA事务,还有hibernate的一套事务管理)-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 自己定义的dao -->
<bean id="mydao" class="spring.senssic.tx.MyDao">
</bean>
</beans>
dao的业务代码:
package spring.senssic.tx;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
public class MyDao {
@Resource(name = "jdbcTemplate")//使用注解的方式由spring自动注入
private JdbcTemplate jt;
public List<Person> findALL() {
@SuppressWarnings("unchecked")
RowMapper<Person> rowMapper = new RowMapper() {
@Override
public Object mapRow(ResultSet rs, int arg1) throws SQLException {
Person person = new Person();
person.setAge(rs.getInt("age"));
person.setName(rs.getString("name"));
return person;
}
};
return jt.query("select * from sen", rowMapper);
}
}
bean类:
package spring.senssic.tx;
public class Person {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package spring.senssic.test;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring.senssic.temp.AopTo;
import spring.senssic.temp.B;
import spring.senssic.temp.C;
import spring.senssic.temp.D;
import spring.senssic.tx.MyDao;
import spring.senssic.tx.Person;
public class STest {
@BeforeClass
public static void TWork() {
ApplicationContext act = new ClassPathXmlApplicationContext("beans.xml");
Object d = act.getBean("dataSource");
System.out.println(d.getClass().getName());
MyDao mDao = (MyDao) act.getBean("mydao");
List<Person> list = mDao.findALL();
for (Person p : list) {
System.out.println("name:" + p.getName() + "--->age:" + p.getAge());
}
}
}
spring对hibernate的支持
Step1: 配置数据源
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/senssic</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>qiyu0126</value>
</property>
</bean>
以上三种的任意一种即可
Step2: 配置sessionfactory
<bean id="mySessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="mappingResources">
<list>
<value>spring/senssic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
Step3: 配置DAO
<bean id="orderDao" class="spring.Senssic.DAOHibernateImpl">
<property name="sessionFactory">
<ref bean="mySessionFactory" />
</property>
</bean>
注意:以上配置是要求dao 继承HibernateDaoSupport
spring对事务的支持
一、AOP事务的含义:
事务当作一个切面,动态地织入到目标对象,形成一个代理对象。
二、Spring的事务机制
Spring支持声明式事务。
Spring使用事务服务代理和事务管理器(如HibernateTransactionManager)来支持事务服务。
Spring对事务的边界多了一种嵌套事务(PROPAGATION_NESTED)。
PROPAGATION_NESTED:
如果客户端启动了事务T1,那么Bean启动一个嵌套在T1中的子事务T2;
如果客户端没有启动事务,那么Bean会启动一个新的事务,类似于REQUIRED_NEW
事务传播属性
NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。
REQUIRESNEW:属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。
MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出例外。
SUPPORTS:这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。
Never:指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出例外,只有业务方法没有关联到任何事务,才能正常执行。
NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效
Spring中使用Hibernate事务
<!-- 配置sessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value> <!--hibernate的配置文件,里面配置了数据源,可以同刚才上面的sessionfactory创建作比较,上面那个指定数据源-->
</property>
</bean>
<!--使用hibernate的事务管理(HibernateTransactionManager对Hibernate的支持,SessionFactory)-->
<bean id="myTransactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="mySessionFactory" />
</property>
</bean>
<!– 采用@Transactional注解方式使用事务 ,且被此注解定义的bean受spring管理--> <tx:annotation-driven transaction-manager="txManager"/>
当标于类前时, 标示类中所有方法都进行事物处理
例子:
@Transactional
public class TestServiceBean implements TestService {}
当类中某些方法不需要事物时:
@Transactional
public class TestServiceBean implements TestService {
private TestDao dao;
public void setDao(TestDao dao) {
this.dao = dao;
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public List<Object> getAll() {
return null;
}
}
事物传播行为介绍:
@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
事物超时设置:
@Transactional(timeout=30) //默认是30秒
事务隔离级别:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED)
读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ)
可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE)
串行化
MYSQL: 默认为REPEATABLE_READ级别
SQLSERVER: 默认为READ_COMMITTED
<!-- 配置sessionFactory
-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
</bean>
<!-- 配置事务管理器
-->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<!-- 配置事务的传播特性
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="modify*" propagation="REQUIRED" />
<tx:method name="*" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 那些类的哪些方法参与事务
-->
<aop:config>
<aop:pointcut id="allManagerMethod" expression="execution(* spring.senssic.manager.*.*(..))" />
<aop:advisor pointcut-ref="allManagerMethod" advice-ref="txAdvice" />
</aop:config>
数据库系统提供了四种事务隔离级
数据库系统提供了四种事务隔离级别供用户选择。不同的隔离级别采用不同的锁类型来实现,在四种隔离级别中,Serializable的隔离级别最高,Read Uncommited的隔离级别最低。大多数据库默认的隔离级别为Read Commited,如SqlServer,当然也有少部分数据库默认的隔离级别为Repeatable Read ,如MysqlRead Uncommited:读未提交数据(会出现脏读,不可重复读和幻读)。
Read Commited:读已提交数据(会出现不可重复读和幻读)
Repeatable Read:可重复读(会出现幻读)
Serializable:串行化
脏读:一个事务读取到另一事务未提交的更新新据。
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。
幻读:一个事务读取到另一事务已提交的insert数据。
Spring事务与EJB事务
1、EJB事务:
EJB的CMT管理事务方式,只能设置事务边界(传播行为),对于隔离性是不能设置的,并且EJB不支持嵌套事务。
2、Spring事务:
对于Spring来说, Spring的声明式事务可以设置事务边界(传播行为),设置隔离级别,设置只读事务,回滚规则(+:对于任何异常都提交,-:对于任何异常都回滚)
<property name=”transactionAttributes”>
<props>
<prop key=”*”>+异常类型1,-异常类型2</prop>
</property>
PS:Spring对嵌套事务的支持依赖与数据库底层对嵌套式事务的支持。