Spring
1.spring的依赖库
*Spring_HOME/dist/spring.jar
*Spring_HOME/lib/jakarta-commons/commons-logging.jar
*Spring_HOME/lib/log4j/log4j-1.2.14.jar
2. copy Sprign的配置文件到src目录下
控制反转(Inversion of Control,IoC)与依赖注入(Dependency injection)
Ioc可以有2种方式:1)依赖查找 JNDI
2)依赖注入
由容器来管理对象之间的依赖关系(而不是对象本身来管理),就是控制反转
或依赖注入。
Spring的注入方式
●接口注入(有侵略性不推荐)
●构造器注入
<beans>
...
<bean id="userManager" class="spring.manager.UserManager">
<constructor-arg ref="userDao4OracleImpl">
</bean>
</beans>
●setter注入:
在UserManger类中提供setter方法,让spring将实例化好的userDao实例话注入给我们
让spring来管理我们的对象创建,和管理我们的对象依赖关系
<beans>
...
<bean id="userDao4MysqlImpl" class="spring.dao.UserDao4MySqlImpl"/>
<bean id="userDao4OracleImpl" class="spring.dao.UserDao4OracleImpl"/>
<bean id="userManager" class="spring.manager.UserManager">
<property name="userDao" ref="userDao4MysqlImpl"/>
</bean>
</beans>
public class client {
public static void main(String[] args){
//通过配置文件初始化bean工厂
BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂得到userManager
UserManager userManager=(UserManager)factory.getBean("userManager");
userManager.save("张三","123");
}
}
属性编辑器,作用
*自定义属性编辑器,是将spring配置文件中的字符串转换成相应的对象进行注入,
spring已经配置了许多编辑器,我们可以自定义自己的属性编辑器
如何自定义属性编辑器?
*继承PropertyEditorSupport类,覆写setAsText()方法,
public class UtilDatePropertyEditor extends PropertyEditorSupport {
private String format = "yyyy-MM-dd";
public void setAsText(String text) throws IllegalArgumentException {
SimpleDateFormat sdf = new SimpleDateFormat(format);
try {
Date date = sdf.parse(text);
this.setValue(date);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
}
*将属性编辑器注册到Spring中
<bean id ="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<!-- 定义属性编辑器-->
<property name="customEditors">
<map>
<entry key="java.util.Date">
<bean class="spring.UtilDatePropertyEditor">
<property name="format" value="yyyy/MM/dd"/>
</bean>
</entry>
</map>
</property>
</bean>
如何将公共的注释定义描述出来
*定义公共的注入属性,通过<bean>标签定义。指定abstract="true"
*具有相同注入属性的类指定parent属性
<bean id="beanAbstract" abstract="true">
<property name="id" value="10"/>
<property name="name" value="Tom"/>
</bean>
<bean id="bean1" class="spring.Bean1" parent="beanAbstract">
<property name="password" value="123"/>
</bean>
<bean id="bean2" class="spring.Bean2" parent="beanAbstract">
<property name="emal" value="abc@emal.com"/>
</bean>
spring bean 的作用域
scope可以取值:
*singleton,每次调用getBean返回相同的实例
*prototype,每次调用getBean返回不同的实例
默认是singleton
Spring的AOP
Spring的事务
事务主要分为:编程式事务、声明式事务
1.sessionFactory.getCurrentSession()和openSession()的区别:
●采用getCurrentSession()创建session,hibernate会绑定到当前的线程中,而openSession()不会。
●采用getCurrentSession()创建的session,事务结束后(commit)会将session关闭
●采用openSession创建的必须手动关闭。
要使用getCurrentSession 在hibernate.cfg.xml中加入
<!--如果是本地事务thread -->
<property name="hibernate.current_session_context_class">thread</property>
<!--如果是全局事务JTA-->
<property name="hibernate.current_session_context_class">jta</property>
声明式事务:
1 声明式事务的配置
1. 配置SessionFactory
2. 配置事务的管理器
3. 配置事务的传播特性
4. 配置事务的哪些类使用
2 编写业务逻辑方法
1. 继承HibernateDaoSupport类,使用HibernateTemplate这个类持久化数据
HibernateTemplate是对session的轻量级封装。
2. 默认事务回滚异常是RuntimeException(包括所有子类),普通异常不回滚。
3. 在编写业务逻辑方法时,最好将异常一直往上抛出,表示层来处理如(struts)
4. spring的事务管理需要添加到业务逻辑对象上(事务边界的定义),不要加到DAO上。
3 事物的传播特性:
1. PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启。
2. PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
3. PROPAGATION_MANDATORY: 如果已存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
4. PROPAGATION_REQUIRES_NEW: 总是开启一个新事物。如果一个事务已经存在,则将这个存在的事务挂起。
5. PROPAGATION_NOT_SUPPORTED: 总是非事务的执行,并挂起任何存在的事务。
6. PROPAGATION_NEVERL: 总是非事务的执行,如果存在一个活动事务,则抛出异常。
(Sping独有) 7. PROPAGATION_NESTED:如果一个活动的事务存在则运行到一个嵌套的事务中,如果没有活动事务,
则按TransactionDefinition.PROPAGATION_REQUIRED属性执行。
4. Spring的事务的隔离级别
(Sping独有) 1. ISOLATION_DEFAULT:这是一个PlatfromTransactionManager默认的隔离级别,
使用数据库默认的事务隔离级别。
2. ISOLATION_READ_UNCOMMITTED:这是事务最低的隔离级别,它允许另外一个事务看到这个
事务未提交的数据。
3. ISOLATION_READ_COMMITTED:保证一个事务修改的数据提交后才能被另外一个事务读取。
另外一个事务不能读取该事务未提交的数据。
4. ISOLATION_REPEATABLE_READ:这种事务级别可以防止脏读,不可重复读。但可以出现幻想读。
它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面情况的产生(不可重复读)。
5. ISOLATION_SERIALIZABLE: 这是花费最高代价但最可靠的事务隔离级别。事务处理为顺序执行。
除了防止脏读,不可重复读外,还避免了幻象读。
下面引用 http://www.blogjava.net/zhengtengfeng/archive/2007/04/23/113025.html
脏读 dirty reads:当事务读取还未被提交的数据时,就会发生这种事件。举例来说:Transaction 1 修改了一行数据,然后 Transaction 2 在 Transaction 1 还未提交修改操作之前读取了被修改的行。如果 Transaction 1 回滚了修改操作,那么 Transaction 2 读取的数据就可以看作是从未存在过的。
不可重复的读 non-repeatable reads:当事务两次读取同一行数据,但每次得到的数据都不一样时,就会发生这种事件。举例来说:Transaction 1 读取一行数据,然后 Transaction 2 修改或删除该行并提交修改操作。当 Transaction 1 试图重新读取该行时,它就会得到不同的数据值(如果该行被更新)或发现该行不再存在(如果该行被删除)。
虚读(幻象读) phantom read:如果符合搜索条件的一行数据在后面的读取操作中出现,但该行数据却不属于最初的数据,就会发生这种事件。举例来说:Transaction 1 读取满足某种搜索条件的一些行,然后 Transaction 2 插入了符合 Transaction 1 的搜索条件的一个新行。如果 Transaction 1 重新执行产生原来那些行的查询,就会得到不同的行。
事务场景 是这样的:
对于同一个银行帐户A内有200元,甲进行提款操作100元,乙进行转帐操作100元到B帐户。如果事务没有进行隔离可能会并发如下问题:
1、第一类丢失更新:首先甲提款时帐户内有200元,同时乙转帐也是200元,然后甲乙同时操作,甲操作成功取走100元,乙操作失败回滚,帐户内最终为200元,这样甲的操作被覆盖掉了,银行损失100元。
2、脏读:甲取款100元未提交,乙进行转帐查到帐户内剩有100元,这是甲放弃操作回滚,乙正常操作提交,帐户内最终为0元,乙读取了甲的脏数据,客户损失100元。
3、虚读:和脏读类似,是针对于插入操作过程中的读取问题,如丙存款100元未提交,这时银行做报表进行统计查询帐户为200元,然后丙提交了,这时银行再统计发现帐户为300元了,无法判断到底以哪个为准?
大家好像觉得统计这个东西肯定是时时更新的,这种情况很正常;但是如果统计是在一个事务中的时候就不正常了,比如我们的一个统计应用需要将统计结果分别输出到电脑屏幕和远程网络某台计算机的磁盘文件中,为了
提高性能和用户响应我们分成2个线程,这时先完成的和后完成的统计数据就可能不一致,我们就不知道以哪个为准了。
4、不可重复读:甲乙同时开始都查到帐户内为200元,甲先开始取款100元提交,这时乙在准备最后更新的时候又进行了一次查询,发现结果是100元,这时乙就会很困惑,不知道该将帐户改为100还是0。
和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。
5、第二类丢失更新:是不可重复读的一种特例,如上,乙不做第二次查询而是直接操作完成,帐户内最终为100元,甲的操作被覆盖掉了,银行损失100元。感觉和第一类丢失更新类似。
在多个事务并发做数据库操作的时候,如果没有有效的避免机制,就会出现种种问题。大体上有三种问题,归结如下:
1、丢失更新
如果两个事务都要更新数据库一个字段X,x=100
事务A 事务B
读取X=100
读取X=100
写入x=X+100
写入x=X+200
事务结束x=200
事务结束x=300
最后x==300
这种情况事务A的更新就被覆盖掉了、丢失了。
丢失更新说明事务进行数据库写操作的时候可能会出现的问题。
2、不可重复读
一个事务在自己没有更新数据库数据的情况,同一个查询操作执行两次或多次的结果应该是一致的;如果不一致,就说明为不可重复读。
还是用上面的例子
事务A 事务B
读取X=100
读取X=100
读取X=100
写入x=X+100
读取X=200
事务结束x=200
事务结束x=200
这种情况事务A多次读取x的结果出现了不一致,即为不可重复读。
再有一情况就是幻影
事务A读的时候读出了15条记录,事务B在事务A执行的过程中删除(增加)了1条,事务A再读的时候就变成了14(16)条,这种情况就叫做幻影读。
不可重复读说明了做数据库读操作的时候可能会出现的问题。
3、脏读(未提交读)
防止一个事务读到另一个事务还没有提交的记录。
如:
事务A 事务B
读取X=100
写入x=X+100
读取X=200
事务回滚x=100
读取X=100
事务结束x=100
x锁 排他锁 被加锁的对象只能被持有锁的事务读取和修改,其他事务无法在该对象上加其他锁,也不能读取和修改该对象
s锁 共享锁 被加锁的对象可以被持锁事务读取,但是不能被修改,其他事务也可以在上面再加s锁。
封锁协议:
一级封锁协议:
在事务修改数据的时候加x锁,直到事务结束(提交或者回滚)释放x锁。一级封锁协议可以有效的防止丢失更新,但是不能防止脏读不可重复读的出现。
二级封锁协议:
在一级封锁的基础上事务读数据的时候加s锁,读取之后释放。二级封锁协议可以防止丢失更新,脏读。不能防止不可重复读。
三级封锁协议:
在一级封锁的基础上事务读数据的时候加s锁,直到事务结束释放。二级封锁协议可以防止丢失更新,脏读,不可重复读。