设为首页 收藏本站
查看: 1350|回复: 0

[经验分享] 基于Mybatis的通用Service层实现

[复制链接]

尚未签到

发表于 2016-11-27 10:18:25 | 显示全部楼层 |阅读模式
  首先抽象实体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;
}
}

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-306082-1-1.html 上篇帖子: Mybatis 3.1中 Mapper XML 文件 的学习详解 . 下篇帖子: mybatis 源码分析之 Mapper接口
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表