JavaEE5实战笔记02JPA持久层的一些问题

1.       JPA关联实体的级联操作问题

在此次联系中需求是这样的,用户实体会关联一个自己的操作记录明细(消费、充值)实体,操作记录明细实体也是双向关联着用户实体。当删除用户的时候,操作明细记录是能随之(这里的能是允许的意思)级联删除的。而操作记录也是要删除的(操作记录这里仅仅是将用户与操作明细做一个桥梁)。主要类关系是:用户实体面对多个操作记录,一个操作记录面对一个操作记录明细实体、并且面对一个用户实体,一个操作记录明细实体面对一个操作记录。实体Bean代码如下

用户实体:UserDTO

package ejb.dto;

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;

/**
 * 用户实体
 * 
 * @author liuyan
 */
@Entity
@Table(name = "webbank_user")
public class UserDTO implements Serializable {

	private static final long serialVersionUID = 1L;

	public UserDTO() {

	}

	@Id
	@GeneratedValue(generator = "system-uuid")
	@GenericGenerator(name = "system-uuid", strategy = "uuid")
	// 主键
	private String id;

	@Column(name = "userName")
	// 用户名
	private String userName;

	@Column(name = "password")
	// 密码
	private String password;

	@Column(name = "userStates")
	// 用户状态
	private int userStates;

	@Column(name = "money")
	// 用户余钱
	private double money;

	@Column(name = "registeTime")
	// 注册时间
	private Timestamp registeTime;

	@OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "userDTO")
	private List<DealDTO> deals;

//省去setter和getter
}
  操作记录实体: DealDTO

package ejb.dto;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;

/**
 * 交易记录信息
 * @author liuyan
 */
@Entity
@Table(name = "webbank_deal")
public class DealDTO implements Serializable {
	
	public DealDTO(){
		
	}
	private static final long serialVersionUID = 4210820165040311694L;

	@Id 
	@GeneratedValue(generator="system-uuid")
	@GenericGenerator(name="system-uuid",strategy = "uuid")
	private String id;

	@ManyToOne(optional = true, cascade = CascadeType.REFRESH)
	@JoinColumn(name = "user_ID")
	private UserDTO userDTO;

	@OneToOne(optional = true, cascade = CascadeType.ALL)
	@JoinColumn(name = "dealInfo_ID")
	private DealInfoDTO dealInfoDTO;

//省去setter和getter

}

 操作记录明细实体: DealInfoDTO

package ejb.dto;

import java.io.Serializable;
import java.sql.Timestamp;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;

/**
 * 交易详细信息
 * @author liuyan
 */
@Entity
@Table(name = "webbank_dealInfo")
public class DealInfoDTO implements Serializable {
	
	public DealInfoDTO(){
		
	}

	private static final long serialVersionUID = -2266758318775332821L;

	@Id 
	@GeneratedValue(generator="system-uuid")
	@GenericGenerator(name="system-uuid",strategy = "uuid")
	private String id;

	@Column(name = "dealDate")
	private Timestamp dealDate;

	@Column(name = "dealStatue")
	private int dealStatue;

	@Column(name = "dealMessage")
	private String dealMessage;

	@OneToOne(optional = true, cascade = CascadeType.ALL)
	private DealDTO dealDTO;

//省去setter和getter

}

 UserDTO实体中的标记:

@OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "userDTO")
private List<DealDTO> deals;

 翻译成咱们通俗易懂的话就是:“如果JPAUserDTO实体进行了操作,那么在所有的操作中如果涉及到了删除UserDTO实体的话,也要将UserDTO实体有关系的DealDTO实体一并、级联地、株连九族地、毫不留情地删除掉”;“加载用户实体的同时也加载操作记录实体集合,不过是延迟加载”;“在多的一端——DealDTO实体有一个字段是维系2者关系的字段。也就是说让多的那一端记住1VSN的关系罢了。”

DealDTO实体中的标记:

	@ManyToOne(optional = true, cascade = CascadeType.REFRESH)
	@JoinColumn(name = "user_ID")
	private UserDTO userDTO;

 用普通话来说就是:“在用户实体与操作记录实体中,操作实体作为多的一端N中每一条操作记录对象必须有一个与之对应的用户记录”;“当对操作记录实体进行更新操作的时候,相应的在一级缓存中对用户实体也要进行级联的更新”;另一个要说明的就是在多的一端维系了1VSN的关系,在底层数据库中维系的字段就是user_ID

此实体的另一个标记

	@OneToOne(optional = true, cascade = CascadeType.ALL)
	@JoinColumn(name = "dealInfo_ID")
	private DealInfoDTO dealInfoDTO;

 咱们也来用普通话说就是:“对DealDTO实体的任何操作都会影响着操作明细实体DealInfoDTO的生存状态。保存DealDTO实体,别忙,它相应的明细实体也得享受这种保存的待遇。删除DealDTO实体,连累着明细实体也难闹厄运!”;“同时,DealDTO实体维系着这种1VS1的关系的字段。”

扫描二维码关注公众号,回复: 827431 查看本文章

DealInfoDTO实体中的标记:

	@OneToOne(optional = true, cascade = CascadeType.ALL)
	private DealDTO dealDTO;

 他就比较舒服了~~自己的所有操作都会影响着DealDTO实体,除此之外没别的意思。当然了DealDTO实体与DealInfoDTO实体,双方互相都不能为空。双方就像杨过和小龙女一样,共同进退、如荣誉共、谁也缺不了谁、你死我也不能独生、你坠崖16年,我就等你16年!

基于以上的JPA配置就能按照先前的需求进行实体的操作了。

2.       UUID的主键生成策略

这次用的是Oracle10g数据库,一般的Oracle是通过索引进行主键的生成的策略,这里想换一种方式就是采用UUID的方式进行主键生成。

在实体注解中加入

	@Id
	@GeneratedValue(generator = "system-uuid")
	@GenericGenerator(name = "system-uuid", strategy = "uuid")
	private String id;

 即是使用UUID生成主键,并且就是在新增保存实体的时候千万不要自己生成主键,底层的Hibernate框架已经为你做了主键的生成和赋值工作。

3.       某些不需要事务传播的需求

需求如下,比如在用户消费的过程中发生了异常信息,那么需要记录到日志记录中。这里存在个问题,遇到消费发生运行时异常的过程需要事务回滚,那么日志实体的持久化操作就不能与消费业务共享同一个事务,当遇到执行保存日志实体的时候需要另开辟一个新的事务。在异常日志实体中的Service层的代码如下

@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class ExceptionLogServiceImpl extends
		BaseServiceImpl<ExceptionLogDTO, String> implements ExceptionLogService {
	
	/**
	 * 注入其他EJB组件
	 */
	@EJB(beanName = "ExceptionLogEAOImpl")
	private ExceptionLogEAO exceptionLogEAO;
	
	public ExceptionLogServiceImpl() {
		super();
		System.out.println("ExceptionLogServiceImpl初始化");
	}
}

 REQUIRES_NEW代表另开辟一个新的事物,具体的事务传播介绍请参考:http://suhuanzheng7784877.iteye.com/blog/908382

1.Required:共享型
若1处在一个事务中调用了2,则2直接和1在同一个事务中。若1没有任何事务,则系统会默认给2创建一个新事务。此策略适合大多数情况。
2.RequiredNew:独立型
若1在一个事务中调用了2,则系统先将1的事务挂起,不管、之后为2设置一个新事务,执行完毕后恢复1的事务。若1没有事务,则会为2新开一个事务。
3.Mandatory:强制共享型
若1处在一个事务中调用了2,则2直接和1在同一个事务中。若1没有任何事务,直接抛出异常——Transaction RequiredException。
4.NotSupported:无助独立型
若1在一个事务中调用了2,则系统先将1的事务挂起,不管、之后2不开启任何事务,执行完毕后恢复1的事务。若1没有事务,2也直接执行。
5. Supported:啃老型
若1处在一个事务中调用了2,则2直接和1在同一个事务中。若1没有任何事务,直接执行2。
6.Never:捣乱型
若1处在一个事务中调用了2,则抛出RemoteException。若1没有任何事务,直接执行2也不会为它开启任何事务。

猜你喜欢

转载自suhuanzheng7784877.iteye.com/blog/1064449