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;
翻译成咱们通俗易懂的话就是:“如果JPA对UserDTO实体进行了操作,那么在所有的操作中如果涉及到了删除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的关系的字段。”
在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也不会为它开启任何事务。