Skip to content

12.智能保存JpaUtil.save(xxx)

Bing edited this page Jul 27, 2018 · 1 revision

简介

Panda2-dorado-jpa包下的JpaUtil.save方法是一个智能保存方法。对dorado传统保存方式的优化。将这类机械性代码进行极大的简化。

为什么智能

智能体现在五个方面:

  1. 支持单个和批量实体对象的保存
  2. 智能选择合适的EntityManagerFactory(或者说数据源),对于开发人员透明
  3. 自动解析判断前台UpdateAction控件提交到后台的脏树,根据脏树中,Entity的状态执行EntityManager的merge、reomve和persit方法
  4. 当Entity的状态为新增时,且Entity的主键类型为String并且为null,自动给主键属性赋值UUID
  5. 支持@Generator注解,通过在实体类字段上添加@Generator注解来实现对智能保存方法的优雅干预,推荐是全局通用级别的干预

接口SavePolicy

定义:

/**
 *@author Kevin.yang
 *@since 2015年5月17日
 */
public interface SavePolicy {
  void apply(SaveContext context);
}

智能保存方法save的内部实现,其实就是SavePolicy的实现类。主要是由DirtyTreeSavePolicy和SmartSavePolicy来组合实现的。

  • DirtyTreeSavePolicy:负责分析和遍历脏树的结构
  • SmartSavePolicy:负责分析Entity的状态执行不同的持久化操作 在某些特色的情况下,你发现DirtyTreeSavePolicy的实现满足我的需求,但是SmartSavePolicy不满足我的需求。此时,你想要替换掉SmartSavePolicy默认实现,你只需要使用save的重载方法save(Object, SavePolicy)方法。如下:
  JpaUtil.save(users, new SavePolicy() {
    public void apply(SaveContext context) {
      Todo.
    }
  })

SaveContext

SavePolicy执行时的上下文。定义如下:

public class SaveContext {
  private Object entity;
  private EntityManager entityManager;
  private Object parent;
	
  @SuppressWarnings("unchecked")
  public <T> T getEntity() {
    return (T) entity;
  }
  public void setEntity(Object entity) {
    this.entity = entity;
  }
	
  @SuppressWarnings("unchecked")
  public <T> T getParent() {
    return (T) parent;
  }
  public void setParent(Object parent) {
    this.parent = parent;
  }
  public EntityManager getEntityManager() {
    return entityManager;
  }
  public void setEntityManager(EntityManager entityManager) {
    this.entityManager = entityManager;
  }
}

保存上下文包含三个属性:

  1. entity,当前要进行保存的实体对象,即迭代当前项
  2. entityManager,保存entity的entityManager
  3. parent 脏树中entity的父节点实体对象,如果entity处于顶层节点,则parent为null。例如:部门与用户的一对多主从结构中,当entity为某个用户对象,此时的parent则为这个用户对应的部门

SmartSavePolicyAdapter

public class SmartSavePolicyAdapter implements SavePolicy {

  @Override
  public void apply(SaveContext context) {
    Object entity = context.getEntity();
    EntityManager entityManager = context.getEntityManager();
    EntityState state = EntityUtils.getState(entity);
    if (EntityState.NEW.equals(state)) {
      beforeInsert(context);
      entityManager.persist(entity);
      afterInsert(context);
    } else if (EntityState.MODIFIED.equals(state) 
       || EntityState.MOVED.equals(state)) {
      beforeUpdate(context);
      entityManager.merge(entity);
      afterUpdate(context);
    } else if (EntityState.DELETED.equals(state)) {
      beforeDelete(context);
      entityManager.remove(entityManager.merge(entity));
      afterDelete(context);
    }
  }
	
  public void beforeDelete(SaveContext context) {
		
  }
	
  public void afterDelete(SaveContext context) {
		
  }
	
  public void beforeInsert(SaveContext context) {
		
  }
	
  public void afterInsert(SaveContext context) {
		
  }
	
  public void beforeUpdate(SaveContext context) {
		
  }
	
  public void afterUpdate(SaveContext context) {
		
  }
}

SmartSavePolicyAdapter这个类的作用是让开发人员不完全重写SmartSavePolicy的同时,实现对SmartSavePoliy类的扩展,通过对SmartSavePolicyAdapter的六个默认实现的空方法的覆盖重写,从而实现功能扩展。根据自己的具体需求,可以选择覆盖重写其中的一个或者多个方法。例如:

  JpaUtil.save(xxx, new SmartSavePolicyAdapter() {
    @override
    public void beforeInsert(SaveContext context) {
      Todo.
    }
  })

@Generator

实体类字段的值生成器。定义在实体类的字段上面,此注解只有在使用JpaUtil.save方法才会有实际作用。通过@Generator注解,我们可以实现实体类中的创建时间和更新时间字段值的自动生成功能。结下来,我们可以看看创建时间生成器如何实现。

  1. 定义接口GeneratorPolicy的实现:
public class CreatedDatePolicy implements GeneratorPolicy {

  @Override
  public void apply(Object entity, Field field) {
    Assert.isAssignable(Date.class, field.getType(), "Field type must be java.util.Date!");
    EntityState state = EntityUtils.getState(entity);
    if (EntityState.NEW.equals(state)) {
      EntityUtils.setValue(entity, field.getName(), new Date());	
    }
  }
}
  1. 使用CreatedDatePolicy
  @Generator(policy = CreatedDatePolicy.class)
  private Date createAt;