一、背景
工作七年,技术水平还停留在java代码基础的增删改查方法上,对于Spring框架向来是需要的时候靠度娘,不需要的时候束之高阁,近来,反思自身,与优秀的人相比自己的差距越来越大,这是一篇痛定思痛的自我学习指南,希望以后走的每一步,都能踏实勇敢。
时间是存在者的时间。 —海德格尔 ·《存在与时间》
二、什么是Spring
3.1 Spring简介
Spring框架是一个开源的J2EE应用程序框架,针对bean的生命周期进行管理的轻量级容器。
3.2 Spring优点
- 方便解耦,简化开发
- 方便编程测试
- 方便和其他程序进行整合
- 方便集成各种优秀框架
- AOP编程的支持
- 降低API的使用难度
3.3 Spring核心
Spring提供了两个核心技术IoC和AOP。
IoC:全称 Inversion of Control(控制反转),把创建对象的过程交给Spring进行管理。初学者学习Spring之前,写代码的通常是以new的方式产生对象,这样当业务功能发生改变时,源码也需要发生改变,模块的耦合性很高。为了解决这个问题,提出了对象的创建控制权由程序转移到外部,这种思想就被称为控制反转。简单来说,在系统开发过程中,设计的类将交由容器去控制,而不是在类的内部去控制,类与类之间的关系将交由容器处理,一个类在需要调用另一个类时,只要调用另一个类在容器中注册的名字就可以得到这个类的实例,与传统的编程方式有了很大的不同,“不用你找,我来提供给你”,这就是控制反转的含义。
AOP:全称 Aspect Orient Programming(面向切面编程),是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发的效率。
3.4 Spring架构
Spring framework是spring生态圈中最基础的项目,是其他思想的根基,我们通常把Spring framework称之为Spring。
四、 如何搭建一个Spring项目
4.1 jar包准备
4.2 创建项目
4.3 引入jar包
4.4 编写代码
创建UserDao
public interface UserDao {
void print();
}
创建UserDaoImpl
public class UserDaoImpl implements UserDao {
@Override
public void print() {
System.out.println("UserDao的print方法");
}
}
创建UserService
public interface UserService {
void print();
}
创建UserServiceImpl
public class UserServiceImpl implements UserService {
UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void print() {
System.out.println("UserService的print方法");
userDao.print();
}
}
创建AppStartConfig
public class AppStartConfig {
public static void main(String[] args) {
// 传统方法:高内聚
UserDao userDao = new UserDaoImpl();
userDao.print();
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(userDao);
userService.print();
// IOC方法:低耦合
// 获取IOC容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("main/resources/applicationContext.xml");
//获取bean,这里的UserService就是我们前面在配置文件中Id处起的名字
UserDao userDaoIoc = (UserDao) ctx.getBean("UserDao");
userDaoIoc.print();
// 关闭容器
ctx.registerShutdownHook();
UserService userServiceIoc =(UserService) ctx.getBean("UserService");
userServiceIoc.print();
// 暴力关闭容器
ctx.close();
}
}
创建applicationContext.xml
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" >
<!--
xml版本信息
xmlns默认命名空间:表示未使用其他命名空间的所有标签的默认命名空间
xmlns:xsi标准命名空间,用于指定自定义命名空间的schema文件,声明就可以使用schemaLocation属性。
-->
<bean id = "UserDao" class = "main.java.dao.impl.UserDaoImpl" >
</bean>
<bean id = "UserService" class = "main.java.service.impl.UserServiceImpl" >
<!--手动注入-->
<!--
Property标签标示配置当前bean的属性,
name属性标示配置哪一个具体的属性,
ref标签标示参照哪一个bean
-->
<property name="userDao" ref="UserDao"></property>
</bean>
</beans>
运行结果:
以上,一个简单IOC示例的代码就完成了。
五、IoC
5.1 IoC概念
IOC:全称 Inversion of Control(控制反转),把创建对象的过程交给Spring进行管理。
学习Spring之前,传统的代码中通常是以new的方式产生对象,当业务功能发生改变时,源码也需要发生改变,模块的耦合性很高。为了解决这个问题,提出了将对象的创建控制权由程序转移到外部,即将对象存入到 Spring,再从 Spring 中获取对象,这种思想被称为控制反转。
5.2 IoC优点
- 解耦和模块化
- 简化代码
- 更好的可测试性
5.3 IoC核心
IoC核心是对Bean进行管理,包含两个操作:
- Spring创建对象
- Spring注入属性
5.4 Bean管理的两种方式
1、XML
①、setter注入
<!-- 1:setter注入-->
<!-- 引入UserDao,创建UserDao对象-->
<bean id = "UserDao" class = "main.java.dao.impl.UserDaoImpl">
<property name="name" value="setter注入"/>
<property name="age" value="11"/>
</bean>
执行结果:
②、构造器注入
<!-- 2:使用构造器注入-->
<!-- 引入UserDao,创建UserDao对象-->
<bean id = "UserDao" class = "main.java.dao.impl.UserDaoImpl">
<constructor-arg name="name" value="构造器注入"/>
<constructor-arg name="age" value="22"/>
</bean>
执行结果:
③、p命名空间注入
<!-- 3:使用p命名空间注入(前提是:无构造方法或无参构造方法)-->
<!-- 引入UserDao,创建UserDao对象-->
<bean id = "UserDao" class = "main.java.dao.impl.UserDaoImpl" p:name="P命名空间" p:age="33" >
</bean>
执行结果:
④、特殊值null
null赋值:不能用value=null赋值,而是在当前property里面设置null标签
<!-- 特殊值处理:null-->
<!-- 引入UserDao,创建UserDao对象-->
<bean id = "UserDao" class = "main.java.dao.impl.UserDaoImpl">
<property name="name" >
<null/>
</property>
<property name="age" value="44"/>
</bean>
执行结果:
⑤、特殊字符<
“<”、“>” 在xml文档中用来定义标签的开始和结束,不能随便使用,遇到此种情况可用如下两种方式解决:
1:转义字符
<!-- 特殊字符<-->
<!-- 引入UserDao,创建UserDao对象-->
<bean id = "UserDao" class = "main.java.dao.impl.UserDaoImpl">
<property name="name" value="<<大连>>">
</property>
<property name="age" value="44"/>
</bean>
2:CDATA
CDATA节中C表示character是文本字符的意思,CDATA表示纯文本数据;xml中看到CDATA节就知道为纯文本,在CDATA节中写什么都可以
<!-- 特殊字符<-->
<!-- 引入UserDao,创建UserDao对象-->
<bean id = "UserDao" class = "main.java.dao.impl.UserDaoImpl">
<property name="name" >
<value><![CDATA[<<大连>>]]></value>
</property>
<property name="age" value="44"/>
</bean>
执行结果:
2、注解
什么是注解
- 注解作用:和xml配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能执行具体操作。本质上来说,所有的操作都是Java代码完成的,xml和注解只是告诉框架的Java代码如何执行。
- 注解格式:@注解名称(属性名称 = 属性值,属性名称 = 属性值…)
- 注解位置:可以使用在类、方法、属性上方
- 注解优点:使用注解可以简化xml文件配置
标识组件的常用注解
- @Component:将类标识为普通组件
- @Controller:将类标识为控制层组件
- @Service:将类标识为业务层组件
- @Repository:将类标识为持久层组件
上述四个注解的功能一样,只是名字不一样,都可以用来创建bean对象。
注意:注解要加到实现类上
注解的自动装配
1、概念
根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean所依赖的类类型或接口类型属性赋值。
2、步骤
- 第一步:引入jar包
- 第二步:applicationContext.xml文件增加扫描组件配置:
<!-- 2.扫描组件-->
<cotext:component-scan base-package="main.java.com"></cotext:component-scan>
- 第三步:类上要写上相对应的注解,以便扫描:
- 第四步:在成员变量上标记@Autowired注解即可完成自动装配,不需要再提供set方法(在项目中也如此用)
//3.将实现类标记为相应标签,以便ioc容器扫描(可以不用在像xml一样写bean)
@Service
public class UserAutowiredServiceImpl implements UserAutowiredService {
//4.在成员变量上标记@AutoWired注解,而不再需要提供set方法
@Autowired
private UserAutowiredDao userAutowiredDao;
public void setUserDao(UserAutowiredDao userAutowiredDao) {
this.userAutowiredDao = userAutowiredDao;
}
@Override
public void saveUser() {
userAutowiredDao.saveUser();
}
}
3、@Autowired注解原理
- 默认通过byType方式,在ioc容器中通过类型匹配某个bean为属性赋值(一般都是这这个就可以)
- 若有多个类型匹配的bean,此时会自动转换为byName的方式实现自动装配的效果;即将要赋值的属性的属性名作为bean的id匹配某个bean为属性赋值
- 若byType和byName的方式都无法实现自动装配,即IOC容器中有多个类型匹配的bean,且这些bean的id和要赋值的属性的属性名都不一致,此时会抛出异常:NoUniqueBeanDefinitionException(这种情况几乎不会发生,一般默认byType就能处理,因为一个类型的bean不会在ioc容器中存在多个)
- 若出现以上情况则可以在要赋值的属性上,添加一个注解@Qualifier;通过该注解的value属性值,指定某个bean的ID,将这个bean为属性赋值
- 若ioc容器中没有任何一个类型匹配的bean,此时抛出异常:noSucnBean;这是因为在@Autowired注解中有一个属性required,默认值为true,要求必须完成自动装配; 若将required设置为false,此时能装配则装配,无法装配则使用属性的默认值。
六、JDBCTemplate
上面已经学习了通过Spring IoC实现对象的创建及管理,接下来要学习,将对象的属性保存到数据库中,实现数据持久化。Spring框架对JDBC进行封装,通过JDBCTemplate实现对数据库的操作。
6.1JDBCTemplate示例
- 引入jar包
- 配置连接池
<!-- 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/lzx"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
- 配置JdbcTemplate对象,注入DataSoucrce
<!--JDBCTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入属性 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
- 创建service类
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void addUser(User user){
userDao.addUser(user);
}
}
- 创建dao类,在dao中注入jdbcTemplate对象,实现增删改查。
@Repository
public class UserDao {
// 注入jdbcTemplate对象
@Autowired
private JdbcTemplate jdbcTemplate;
public Map<String,Object> addUser(User user){
Map<String,Object> resultMap = new HashMap<>();
//创建sql语句
String sql = "insert into user values(?,?,?,?,?)";
//调用方法实现
Object[] args = {user.getUserId(),user.getUserCode(),user.getUserName(),user.getAge(),user.getPhone()};
int update = jdbcTemplate.update(sql, args);
System.out.println(update);
return resultMap;
}
}
- 在启动类中调用方法
public class AppStartConfig {
public static void main(String[] args) {
// 获取IOC容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("main/resources/applicationContext.xml");
UserService userService = (UserService) ctx.getBean("userService");
//测试新增
User user = new User();
user.setUserId("123456789");
user.setUserCode("hahahahaha");
user.setUserName("哈哈哈哈哈哈哈");
user.setAge(28);
user.setPhone("13100000000");
userService.addUser(user);
userService.selectAllUser();
//测试修改
user.setUserName("嘿嘿嘿嘿嘿嘿嘿");
userService.updateUser(user);
userService.selectAllUser();
//测试删除
userService.deleteUser("123456789");
//测试查看
userService.selectAllUser();
}
}
6.2JDBCTemplate主要方法
- execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句;
- update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;batchUpdate方法用于执行批处理相关语句;
- query方法及queryForXXX方法:用于执行查询相关语句;
- call方法:用于执行存储过程、函数相关语句。
七、事务管理
7.1、什么是事务
(1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
(2)典型场景:银行转账.
lucy 转账 100 元 给 mary
lucy 少 100,mary 多 100
7.2、事务的四个特性(ACID)
原子性(Atomiticy)
原子性是指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。举例来说,假设用户A和用户B两者的钱加起来一共是1000,那么不管A和B之间如何转账、转几次账,事务结束后两个用户的钱相加起来应该还得是1000,这就是事务的一致性。
隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如同时操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
持久性是指在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。 即使出现了任何事故比如断电等,事务一旦提交,则持久化保存在数据库中。
7.3 Spring 事务管理
在事务管理方面,主要有两个分类:
编程式事务
在代码中直接加入处理事务的逻辑,可能需要在代码中显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法。
声明式事务
在方法的外部添加注解或者直接在配置文件中定义,将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。Spring的AOP恰好可以完成此功能:事务管理代码的固定模式作为一种横切关注点,通过AOP方法模块化,进而实现声明式事务。声明式事务有两种实现方式:
基于注解方式(使用)
不需要配置文件 用配置类取代配置文件
- 创建配置类,使用配置类替代 xml 配置文件
- 在方法上开启事务注解
@Configuration //配置类
@ComponentScan(basePackages = "main.java.com.user") //组件扫描
@EnableTransactionManagement //开启事务
public class UserConfig {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://192.168.181.27:3306/ccop-saas?serverTimezone=GMT%2B8");
dataSource.setUsername("root");
dataSource.setPassword("paas@2021");
return dataSource;
}
//创建JdbcTemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
//到ioc容器中根据类型找到dataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入dataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
// 创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
// 新增
@Transactional
public void addUser(User user){
userDao.addUser(user);
}
基于 xml 配置文件方式
在 spring 配置文件中进行配置:
- 引入tx名称空间,schemaLocation声明空间地址
- 配置事务管理器
- 配置通知
- 配置切入点和切面
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:cotext="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--
xmlns:tx 引入tx名称空间,schemaLocation声明空间地址
-->
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource">
</property>
</bean>
<!-- 配置通知-->
<tx:advice id="txadvice">
<!--配置事务参数-->
<tx:attributes>
<!--指定哪种规则的方法上面添加事务-->
<tx:method name="addUser" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置切入点和切面-->
<aop:config>
<!-- 配置切入点-->
<aop:pointcut id="pt" expression="execution(* main.java.com.user.Service.UserService.*(..))"/>
<!-- 配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
注意:将@Transactional标注在类上,这个类的所有方法都添加事务
注意:将@Transactional标注在方法中,这个方法添加事务
声明式事务管理参数配置
Propagation:事务传播行为
事务的传播行为可以由传播属性指定,Spring指定了7种类传播行为
isolation:事务隔离级别
为什么要设置隔离级别
在数据库操作中,在并发的情况下可能出现如下问题:
更新丢失(Lost update)
如果多个线程操作,基于同一个查询结构对表中的记录进行修改,那么后修改的记录将会覆盖前面修改的记录,前面的修改就丢失掉了,这就叫做更新丢失。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。
第1类(撤销)丢失更新:事务A撤销时,把已经提交的事务B的更新数据覆盖了。
第2类(提交)丢失更新:事务A提交时,把已经提交的事务B的更新数据覆盖了。
解决方法:对行加锁,只允许并发一个更新事务。
脏读(Dirty Reads)
脏读(Dirty Read):A事务读取B事务尚未提交的数据并在此基础上操作,而B事务执行回滚,那么A读取到的数据就是脏数据。
解决办法:如果在第一个事务提交前,任何其他事务不可读取其修改过的值,则可以避免该问题。
不可重复读(Non-repeatable Reads)
一个事务对同一行数据重复读取两次,但是却得到了不同的结果。事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。
解决办法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。
幻象读
指两次执行同一条 select 语句会出现不同的结果,第二次读会增加一数据行,并没有说这两次执行是在同一个事务中。一般情况下,幻象读应该正是我们所需要的。但有时候却不是,如果打开的游标,在对游标进行操作时,并不希望新增的记录加到游标命中的数据集中来。隔离级别为 游标稳定性 的,可以阻止幻象读。例如:目前工资为1000的员工有10人。那么事务1中读取所有工资为1000的员工,得到了10条记录;这时事务2向员工表插入了一条员工记录,工资也为1000;那么事务1再次读取所有工资为1000的员工共读取到了11条记录。
事务的隔离级别
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted(未授权读取、读未提交)、Read committed(授权读取、读提交)、Repeatable read(可重复读取)、Serializable(序列化),这四个级别可以逐个解决脏读、不可重复读、幻象读这几类问题。
- Read uncommitted(未授权读取、读未提交)
如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。这样就避免了更新丢失,却可能出现脏读。也就是说事务B读取到了事务A未提交的数据。 - Read committed(授权读取、读提交)
读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。该隔离级别避免了脏读,但是却可能出现不可重复读。事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。 - Repeatable read(可重复读取)
可重复读是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,即使第二个事务对数据进行修改,第一个事务两次读到的的数据是一样的。这样就发生了在一个事务内两次读到的数据是一样的,因此称为是可重复读。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。这样避免了不可重复读取和脏读,但是有时可能出现幻象读。(读取数据的事务)这可以通过“共享读锁”和“排他写锁”实现。 - Serializable(序列化)
提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。序列化是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。大多数数据库的默认级别就是Read committed,比如Sql Server , Oracle。MySQL的默认隔离级别就是Repeatable read。
悲观锁和乐观锁
虽然数据库的隔离级别可以解决大多数问题,但是灵活度较差,为此又提出了悲观锁和乐观锁的概念。
悲观锁
悲观锁,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制。也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统的数据访问层中实现了加锁机制,也无法保证外部系统不会修改数据。
实现方式:
我们可以使用命令设置MySQL为非autocommit模式:set autocommit=0;
设置完autocommit后,我们就可以执行我们的正常业务了。具体如下:
//0.开始事务
begin;/begin work;/start transaction; (三者选一就可以)
//1.查询出商品信息
select status from t_items where id=1 for update;
//2.根据商品信息生成订单
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status为2
update t_items set status=2;
//4.提交事务
commit;/commit work;
乐观锁
乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以只会在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回用户错误的信息,让用户决定如何去做。实现乐观锁一般来说有以下2种方式:
使用版本号
使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
使用时间戳
乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。
timeOut:超时时间
事务必须在一定时间内进行提交,若不提交则回滚;
默认值为-1,设置时间以秒单位进行计算
readOnly:是否只读
读:查询数据,写:添加、修改、删除数据
readOnly默认是false,表示可以进行CRUD
readOnly的值设置为true,则只能读
rollbackFor:回滚
noRollbackFor:不回滚
八、AOP
参考文档
Spring入门,看这篇就够了
Spring学习(汇总版)
Spring中xml文件与注解
Spring 基础概念和核心思想
事务的四大特性
SpringAOP(图文详解)