欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于Mybatis的通用Service层实现

程序员文章站 2022-05-17 19:37:07
...

首先抽象实体Bean的父类BaseModel,包括通用的创建时间、分页等基本信息:

public abstract class BaseModel implements Serializable {
    private static final long serialVersionUID = -665036712667731957L;

    /**
     * 排序 升 降
     */
    private String order;
    /**
     * 排序字段
     */
    private String orderBy;
    private String orderType;
    /**
     * 分页用当前页号
     */
    private Integer page = 1;
    /**
     * 分页用记录开始位置
     */
    private Integer startPos;
    /**
     * 分页用页面大小
     */
    private Integer pageSize = 20;

    /**
     * 记录创建时间
     */
    private Date createTime;
    /**
     * 记录最后一次修改时间
     */
    private Date updateTime;
    /**
     * 创建人ID
     */
    private Integer creatorID;
    /**
     * 创建人用户名
     */
    private String creatorUserName;
    /**
     * 创建人姓名
     */
    private String creatorName;

    public abstract Object getId();

    @Override
    public String toString() {
        ToStringBuilder builder = new ToStringBuilder(this);
        Field[] fields = this.getClass().getDeclaredFields();
        try {
            for (Field f : fields) {
                f.setAccessible(true);
                builder.append(f.getName(), f.get(this));
            }
        } catch (Exception e) { // Suppress
            builder.append("toString builder encounter an error");
        }
        return builder.toString();
    }
}

 之后定义一个通用的泛型化的DAO接口,该接口里包含了比较通用的CRUD操作的方法声明。通过继承该接口,使你的DAO接口免去声明这些比较通用的CRUD方法的工作。

public interface IGenericDao<T extends BaseModel, ID extends Serializable> {
    /**
     * 添加新实体
     */
    void save(T t);

    /**
     * 批量添加新实体
     */
    void batchSave(List<T> list);

    /**
     * 删除实体(软册除status=2)
     */
    void delete(ID id);

    /**
     * 批量删除实体(软删除status=2)
     */
    void batchDelete(List<ID> list);

    /**
     * 修改实体
     */
    void update(T t);

    /**
     * 通过ID获取实体
     */
    T get(ID id);

    /**
     * <p>
     * 带分页的查询列表,与分页相关的语句需要自己编写,mybatis将不会干扰。
     * </p>
     */
    PaginatedArrayList<T> listByLimit(T t);

    /**
     * <p>
     * 不带分页的列表查询。
     * </p>
     */
    List<T> list(T t);

    /**
     * 通过id列表获取实体列表
     */
    List<T> getbyIdList(@Param("ids") List<ID> list);

    /**
     * 根据条件查记录数
     */
    int count(T t);
}

 这样具体业务实体的DAO接口直接继承IGenericDAO即可,当然也可以添加其他的方法,比如根据用户角色查询用户列表:

public interface IUserDAO extends IGenericDao<User, Integer> {
    /**
     * 根据角色获取所有用户
     */
    List<User> getUserByRoleId(Integer roleId);
}

 通用的Service接口与DAO接口基本一样,下面代码是通用Service接口的抽象实现类:

public abstract class AbstractGenericService<T extends BaseModel, ID extends Serializable> implements
        GenericService<T, ID> {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractGenericService.class);

    @SuppressWarnings("unchecked")
    private Class<T> getTClass() {
        return ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
    }

    @Resource
    protected GenericCacheManager cacheManager;


    @Resource
    private TaskExecutor taskExecutor;

    public abstract IGenericDao<T, ID> getDao();

    @Override
    public void save(T t) {

        if (t == null) {
            LOG.info("待插入的实体为null,class:{}", this.getTClass().getName());
            return;
        }

        this.getDao().save(t);
    }

    @Override
    public void saveOrUpdate(T t) {
        if (t == null) {
            return;
        }

        if (t.getId() == null) {
            this.save(t);
        } else {
            this.update(t);
        }
    }

    /**
     * 删除实体(软册除status=2)
     *
     * @param id
     * @throws Exception
     */
    @Override
    @Transactional
    public void delete(ID id) {

        if (id == null) {
            return;
        }

        this.getDao().delete(id);
        this.cacheManager.remove(CacheKeyHelper.ENTITY_REGION, this.makeCacheKey(id));
    }

    /**
     * 批量删除实体
     */
    @Override
    @Transactional
    public void batchDelete(List<ID> list) {

        if (list == null || list.size() <= 0) {
            return;
        }

        this.getDao().batchDelete(list);

        // 从缓存中删除id所管理的实体
        for (ID id : list) {
            this.cacheManager.remove(CacheKeyHelper.ENTITY_REGION, this.makeCacheKey(id));
        }
    }

    /**
     * 修改实体
     */
    @Override
    @Transactional
    public void update(T t) {
        if (t == null) {
            LOG.info("待更新的实体为null,class:{}", this.getTClass().getName());
            return;
        }

        // TODO 此处应该填充上修改时间,但是需要BaseModel中有modifytime字段,且子类都继承该字段

        this.getDao().update(t);

        // 从缓存中删除实体,实体会在get的时候再次填入到缓存中
        this.cacheManager.remove(CacheKeyHelper.ENTITY_REGION, this.makeCacheKey(t.getId()));
    }

    /**
     * 通过ID获取实体
     */
    @Override
    @SuppressWarnings("unchecked")
    public T get(ID id) {

        if (id == null) {
            return null;
        }

        // 从缓存中读取实体
        T t = (T) this.cacheManager.get(CacheKeyHelper.ENTITY_REGION, this.makeCacheKey(id));

        if (t != null) {
            return t;
        }

        // 未从缓存中读取到则从数据库中读取实体
        t = this.getDao().get(id);

        if (t != null) {
            this.cacheManager.put(CacheKeyHelper.ENTITY_REGION, this.makeCacheKey(t.getId()), t);
        }

        return t;
    }

    @Override
    public T getDetail(ID id) {
        T t = this.get(id);

        if (t == null) {
            return null;
        }

        this.fillDetail(t);
        return t;
    }

    /**
     * <p>
     * 带分页的列表查询。
     * </p>
     */
    @Override
    public PaginatedList<T> listByLimit(T t) {

        if (t == null) {
            return new PaginatedArrayList<T>(0, 0, 0);
        }

        // 查询数据库中记录的总数
        int total = this.getDao().count(t);

        // 构造带有分页信息的List
        PaginatedList<T> resultList = new PaginatedArrayList<T>(total, t.getPage(), t.getPageSize());
        t.setStartPos(resultList.getStartPos());

        List<T> queryResultList = this.getDao().listByLimit(t);
        resultList.addAll(queryResultList);

        return resultList;
    }

    @Override
    public PaginatedList<T> listDetailByLimit(T t) {
        PaginatedList<T> resultList = this.listByLimit(t);
        for (T item : resultList) {
            this.fillDetail(item);
        }
        return resultList;
    }

    /**
     * <p>
     * 不带分页的列表查询。
     * </p>
     */
    @Override
    public List<T> list(T t) {
        return this.getDao().list(t);
    }

    @Override
    public List<T> listDetail(T t) {
        List<T> resultList = this.list(t);
        for (T item : resultList) {
            this.fillDetail(item);
        }
        return resultList;
    }

    /**
     * 通过id列表获取实体列表
     */
    @Override
    @SuppressWarnings("unchecked")
    public List<T> getbyIdList(List<ID> list) {

        if (list == null || list.size() <= 0) {
            return Collections.EMPTY_LIST;
        }

        List<T> resultList = new ArrayList<T>();
        List<ID> missedIds = new ArrayList<ID>();

        // 先从缓存中读取实体
        T t;
        for (ID id : list) {
            if (id == null) {
                continue;
            }

            // 根据id从缓存中读取实体信息
            t = (T) this.cacheManager.get(CacheKeyHelper.ENTITY_REGION, this.makeCacheKey(id));

            if (t != null) {
                resultList.add(t);
            } else {
                missedIds.add(id); // 未从缓存中读取到实体,则将该实体的id放入到missedIds列表中,稍后从数据库中读取这些实体
            }
        }

        // 如果有些实体未从缓存中取到
        if (missedIds.size() > 0) {

            // 则从数据库中读取这些实体
            List<T> missedModels = this.getDao().getbyIdList(missedIds);

            // 如果数据库中有,则添加到缓存中,然后返回
            if (missedModels != null) {
                for (T model : missedModels) {
                    this.cacheManager.put(CacheKeyHelper.ENTITY_REGION, this.makeCacheKey(model.getId()), model);
                }
                resultList.addAll(missedModels);
            }
        }
        return resultList;
    }

    /**
     * 根据条件查记录数
     */
    @Override
    public int count(T t) {
        return this.getDao().count(t);
    }

    /**
     * <p>
     * 生成cache中该实体的key。 key生成的规则为: 实体短类名 + ":" + 实体id.
     * 例如:id为123的代理商信息的实体在缓存中的key即为:Agent:123 . 子类可以覆盖该方法以生成特殊的key。
     * </p>
     */
    protected String makeCacheKey(Object id) {
        return CacheKeyHelper.getEntityCacheKey(this.getTClass(), id);
    }

    /**
     * 填充引用信息,抽象类中默认不做任何操作,如需填充引用信息,在子类中覆盖此方法
    protected void fillDetail(T t) {
    }

    @Override
    public <M extends BaseModel> void fillListDetailByMultiThread(List<M> list,
            final FillDetailable<M> fillDetailable) {
        if (!CollectionUtils.isEmpty(list)) {
            Integer size = list.size();
            final CountDownLatch latch = new CountDownLatch(size);
            for (final M u : list) {
                taskExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            fillDetailable.fillDetail(u);
                        } finally {
                            latch.countDown();
                        }
                    }
                });
            }
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
                LOG.error(e.getMessage());
            }
        }
    }
}

 这样使用了缓存,可以是如Ehcache等JVM缓存,也可以使用Memcached等分布式缓存,由于会有一些联表查询,实体Bean中会有一些冗余字段,使用fillDetail()方法来进行相应设置。为了提高效率,使用多线程的方式对列表中的实体对象进行fillDetail操作,因此需要FillDetailable接口:

public interface FillDetailable<T extends BaseModel> {
    void fillDetail(T t);
}

 具体的Service实现是需要继承AbstractGenericService即可:

@Service("userService")
public class UserServiceImpl extends AbstractGenericService<User, Integer> implements IUserService {
    protected static final Logger LOG = LoggerFactory.getLogger(UserServiceImpl.class);
    @Autowired
    protected IUserDAO userDAO;

    @Override
    public void fillDetail(User user) {
        UserDepartment filter = new UserDepartment();
        filter.setUserId(user.getId());
        List<UserDepartment> userDepartmentList = userDepartmentService.list(filter);
        final List<Integer> deptIds = new ArrayList<Integer>();
        final List<Integer> deptLevels = new ArrayList<Integer>();
        final List<String> deptNames = new ArrayList<String>();

        this.fillListDetailByMultiThread(userDepartmentList, new FillDetailable<UserDepartment>() {
            @Override
            public void fillDetail(UserDepartment userDepartment) {
                Department department = departmentSerive.get(userDepartment.getDepartmentId());
                if (department != null) {
                    userDepartment.setDepartmentName(department.getName());
                    deptIds.add(userDepartment.getDepartmentId());
                    deptLevels.add(userDepartment.getIndeptlevel());
                    deptNames.add(department.getName());
                }
            }
        });
        user.setDeptIds(deptIds);
        user.setDeptLevels(deptLevels);
        user.setDeptNames(deptNames);
        user.setUserDepartmentList(userDepartmentList);
        List<Role> roles = roleService.getUserRoles(user.getId());
        if (roles != null) {
            user.setRoles(roles);
        }
    }

    @Override
    public IGenericDao<User, Integer> getDao() {
        return userDAO;
    }
}

 

相关标签: 模板模式