概述
声明:小弟也是一个新手,如果有表述不清楚或者不正确的地方请多指正。
方法1:这个是Hibernate的一个乐观锁的实现。是从数据库角度保证数据库的一致性的。
原理是:在数据库中创建一个字段,为version,类型为int,并置默认值为0. hibernate从数据库中找出该条记录的时候,会保存该条记录version。 在操作完该条记录后,往数据库中更新该条记录的时候,会再从数据库中查找一次该条记录的version。并且把第二次的version跟第一次保存的version进行对比。如果两个version相同,则进行更新操作。把version的数值加1,如果两个version不同,则进行事务的回滚操作。
优点:
- 它提供了一个方便,以自动化的方式来保持一致性,像上述的案例。这意味着,每一个动作只能执行一次,它保证用户或服务器过程中看到的是最新状态。
- 它需要很少的工作来设置。
- 由于其乐观的天性,速度非常快。没有锁定的任何地方,只是多了一个字段添加到查询中。
1.要注意的是,由于乐观锁定是使用系统中的程式来控制,而不是使用资料库中的锁定机制,因而如果有人特意自行更新版本讯息来越过检查,则锁定机制就会无效,例如在上例中自行更改stu的version属性,使之与资料库中的版本号相同的话就不会有错误,像这样版本号被更改,或是由于资料是由外部系统而来,因而版本资讯不受控制时,锁定机制将会有问题,设计时必须注意。
2.如果手工设置stu.setVersion()自行更新版本以跳过检查,则这种乐观锁就会失效,应对方法可以将Student.Java的setVersion设置成private
示例:
数据表:
DROP TABLE IF EXISTS `ac_mall_awards`;
CREATE TABLE `ac_mall_awards` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`award_id` bigint(20) DEFAULT NULL,
`total` int(11) DEFAULT NULL,
`remainder` int(11) DEFAULT NULL,
`score` float DEFAULT NULL,
`saleing` int(11) DEFAULT NULL,
`ext` text,
`status` int(11) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`version` bigint(20) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
实体;
@Entity
@Table(name = "ac_mall_awards")
public class MallAwardPO extends EntityModel {
@ManyToOne
@JoinColumn(name = "award_id")
private AwardPO award;
@Column(name = "total")
private int total;
@Column(name = "remainder")
private int remainder;
@Column(name = "score")
private float score;
@Column(name = "saleing")
private int saleing;
@Column(name = "status")
private int status;
@Column(name = "ext")
private String ext;
@Version
private long version;
public AwardPO getAward() {
return award;
}
public void setAward(AwardPO award) {
this.award = award;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public int getRemainder() {
return remainder;
}
public void setRemainder(int remainder) {
this.remainder = remainder;
}
public float getScore() {
return score;
}
public void setScore(float score) {
this.score = score;
}
public int getSaleing() {
return saleing;
}
public String getSaleingDesc() {
return EnumStatusUtils.getStatus(EnumMallAwardSaleingStatus.class, saleing).getDesc();
}
public void setSaleing(int saleing) {
this.saleing = saleing;
}
public int getStatus() {
return status;
}
public String getStatusDesc() {
return EnumStatusUtils.getStatus(EnumMallAwardStatus.class, status).getDesc();
}
public void setStatus(int status) {
this.status = status;
}
public String getExt() {
return ext;
}
public void setExt(String ext) {
this.ext = ext;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
2.使用JAVA 中的 synchronized关键字,synchronized是一种同步锁。
它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
在以下示例中,使用了synchronized关键字修饰的代码块 synchronized(lock) {}后面 大括号扩住的部分。
当线程访问该代码块时候,会判断该代码块是否有人在使用。
如果有人在使用则等待,如果没人使用,则进入该代码块。
如果该代码块中任意一行报错或者执行错误,则进行数据的回滚操作。因为对该Service声明了Spring的事务注解@ Transactional。
如果要详细了解synchronized和Spring的Transactional机制请看小弟的前三篇博客:
java synchronized详解:http://blog.csdn.net/yueaini10000/article/details/51801972
Java中Synchronized的用法:http://blog.csdn.net/yueaini10000/article/details/51802064
Spring @Transactional工作原理:http://blog.csdn.net/yueaini10000/article/details/51802341
示例:
接上面示例1的实体
/**
*
* @author Bruce
* 领取商品业务逻辑
*/
@Service
@Transactional
public class MallAwardReceiveService {
private static Logger logger = Logger.getLogger(MallAwardReceiveService.class);
@Autowired
private MallAwardManager mallAwardManager;
@Autowired
private ScoreService scoreService;
@Autowired
private MallAwardLogManager mallAwardLogManager;
@Autowired
private UserAwardManager userAwardManager;
@Autowired
private MyScoreService myScoreService;
@Autowired
private UserTakeOverAddressService userTakeOverAddressService;
private Object lock = new Object();
/**
* 领取商品业务逻辑
* @return
*/
public void receiveMallAward(long mallAwardId) {
UserContext userContext = UserContextUtils.getUserContextInThreadLocal();
if(userContext.isGuest()) {
throw new ServiceException("需要登录后才能进行商品兑换!");
}
/*1.获取用户积分并检查,可兑换积分是否足够 如果不足返回不足信息
*2.获取兑换商品总数量,如果为-1,则没有限制,如果不为-1,则获取剩余数量 ,剩余数量,如果不足,返回不足信息
*3.检查商品是否过期,
*4.根据mallawardid 减少mallaward商品数量,生成mallawardlogs日志
*5.根据用户ID查询ac_user_addresses信息,如果为空则提示需要添加地址信息
*6.生成 ac_user_awards 表信息
*/
synchronized(lock) {
//持久获奖品信息到数据库
try {
//1.获取用户积分并检查,可兑换积分是否足够 如果不足返回不足信息
ScorePO myScore = myScoreService.getMyScore();//用户积分信息表
MallAwardPO mallAwardPo = mallAwardManager.getById(mallAwardId);//积分商城表
if(myScore == null ||myScore.getExchangeableScore() < mallAwardPo.getScore()){
throw new ServiceException("剩余可兑换积分不足!");
}
//2.获取兑换商品总数量,如果为-1,则没有限制,如果不为-1,则获取剩余数量 ,剩余数量,如果不足,返回不足信息
if(mallAwardPo.getTotal() !=-1 && (mallAwardPo.getRemainder()<=0)){
throw new ServiceException("商品剩余数量不足!");
}
//3.检查商品是否过期
//两个Date类型的变量可以通过compareTo方法来比较。
//此方法的描述是这样的:如果参数 Date 等于此 Date,则返回值 0;
//如果此 Date 在 Date 参数之前,则返回小于 0 的值;
//如果此 Date 在 Date 参数之后,则返回大于 0 的值。
// if((new Date().compareTo(mallAwardPo.getAward().getEnabledAt())) > 0){
// throw new ServiceException("商品已过兑换期限!");
// }
//5.根据用户userid 查询ac_user_addresses,是否存在,如果存在则获取ID,如果不存在,则提示去完善信息
//用户地址信息
UserTakeOverAddressPO userTakeAddressPo = userTakeOverAddressService.getDefaultUserTakeOverAddress(userContext.getUserId());
if(userTakeAddressPo == null ){
throw new ServiceException("请完善个人配送地址信息!");
}
/*
if(!userAddressService.isUserAddressByUserId(userContext.getUserId())){
throw new ServiceException("请完善个人配送地址信息!");
}
*/
//6.生成 ac_user_awards 表信息(
//UserAddressPO userAddressPo = userAddressRepository.findByUserId(userContext.getUserId());
UserAwardPO userAwardPo=new UserAwardPO();
userAwardPo.setUserId(userContext.getUserId());
userAwardPo.setAward(mallAwardPo.getAward());
userAwardPo.setType(mallAwardPo.getAward().getType());
//userAwardPo.setUserAddress(userAddressPo);
userAwardPo.setStatus(EnumUserAwardStatus.NOTSHIP.getValue());
userAwardPo.setSource(EnumUserAwardSource.SOURCE_AWARD.getValue());
userAwardPo.setEnabledAt(DateUtils.getBeforDay(new Date(), 60));
//4.根据mallawardid 减少mallaward商品数量,减少用户可兑换积分,生成mallawardlogs日志
//01.减少mallaward商品数量
if(mallAwardPo.getTotal() != -1 && (mallAwardPo.getRemainder()>0)){
mallAwardPo.setRemainder(mallAwardPo.getRemainder()-1);
}
mallAwardPo.setUpdatedAt(new Date());
//03.生成mallawardlogs日志
MallAwardLogPO mallAwardLogPO =new MallAwardLogPO();
mallAwardLogPO.setPrevScore(myScore.getExchangeableScore());
//02.减少用户可兑换积分
scoreService.changeUserScore(userContext.getUserId(), EnumExchangeableStatus.EXCHANGEABLE, -1*mallAwardPo.getScore(), "商城积分兑换", null);
mallAwardLogPO.setUserId(myScore.getUserId());
mallAwardLogPO.setMallAward(mallAwardPo);
mallAwardLogPO.setAward(mallAwardPo.getAward());
mallAwardLogPO.setNumber(1);
mallAwardLogPO.setUsedScore(mallAwardPo.getScore());
mallAwardLogPO.setIp(userContext.getIp());
mallAwardLogPO.setCreatedAt(new Date());
mallAwardLogPO.setCreatedAt(new Date());
mallAwardManager.save(mallAwardPo);
logger.info("更新sc_score数据成功:" + myScore.toString());
mallAwardLogManager.save(mallAwardLogPO);
logger.info("插入ac_mall_award_logs数据成功:" + mallAwardLogPO.toString());
userAwardManager.save(userAwardPo);
logger.info("插入ac_user_awards数据成功:" + userAwardPo.toString());
} catch (DataAccessException exp) {
logger.error(exp.getMessage(), exp);
throw new ServiceException("领取失败!");
}
}
}
}
最后
以上就是闪闪雨为你收集整理的Spring和Hibernate 下如何保持事务的一致性和并发控制。的全部内容,希望文章能够帮你解决Spring和Hibernate 下如何保持事务的一致性和并发控制。所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复