组织结构
实体类
建议把实体类统一放在某个包下,包名建议取名:
- model(模型,也就是MCV模式中的M,Django也是取的该值)
- entity(Java有些封装的函数参数就是用的这个,也比较推荐)
- bean(java中的bean就是模型层的类,意即model)
- pojo(Plain Ordinary Java Object的缩写;不含业务逻辑的java简单对象;完全POJO的系统也称为轻量级系统(lightweight);是相对于EJB而提出的概念。)
实体操作类
用来提供数据库操作接口:
- JPA中带Repository后缀,如UserRepository
- MyBatis中带Mapper后缀,如UserMapper
对于通用的操作接口,也可以让实体操作类统一继承一个自己封装的中间层(封装一些常用的接口函数):
- JPA的做法是继承JpaRepository
- MyBatis的做法是Mapper、MySqlMapper
例如使用MyBatis时,可以提供一个封装后的通用类CommonMapper:
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;
import java.util.List;
import java.util.Map;
//FIXME 特别注意,该接口不能被扫描到,否则会出错
public interface CommonMapper<T> extends Mapper<T>, MySqlMapper<T> {
List<T> queryList(Map<String, Object> map);
}
public interface CourseMapper extends CommonMapper<Course> {
}
意味着实体操作类承诺提供一个queryList的接口
服务接口
一开始很不理解这种模式,原本业务逻辑层可以直接使用实体操作类就完成的事情,硬是弄出许多个类(算上实体类,实体操作类,服务接口,服务实现一共四个了),但是后来发现这种方式还是有好处的,慢慢习惯就好。
服务接口相当于是提供了一个缓冲地带,隔离了业务逻辑层直接与实体操作类的接触,使得后期调整操作接口或者优化会比较方便。
graph LR
A(实体类)-->B(实体操作类)
B-->C(服务接口 缓冲隔离)
C-->D(业务控制层)
服务接口主要用于暴露给逻辑控制层的,等于告诉逻辑控制层:我有这些接口可以使用,可以在业务中尽情使用。实际应用发现,业务层需要的接口可能无非就是那几个,如果每次创建服务都去写一遍接口,有点重复,那么怎么办呢?
好办,把重复使用的那些接口封装出去,提到一个基类接口中管理,我们把这个基类服务接口叫做BaseService,例如最基本的增删改查这几个接口很多业务都需要,就把这些接口复用起来,让基类来统一实现和管理,接口如下:
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 通用接口
*/
@Service
public interface BaseService<T> {
T selectByKey(Object key);
int save(T entity);
int delete(Object key);
int update(T entity);
//TODO 其他...
}
import com.example.dao.model.Course;
import com.example.util.Page;
import java.util.List;
import java.util.Map;
public interface CourseService extends BaseService<Course> {
int deleteByIds(String[] ids);
int deleteByIds(Long[] ids);
List<Course> queryList(Map<String, Object> params);
List<Course> queryList(Page<?> page);
}
其实现代码BaseServiceImpl如下:
import org.springframework.beans.factory.annotation.Autowired;
import tk.mybatis.mapper.common.Mapper;
import java.util.List;
/**
* 通用Service
*
* @param <T>
*/
public abstract class BaseServiceImpl<T> implements BaseService<T> {
@Autowired
protected Mapper<T> mapper;
public Mapper<T> getMapper() {
return mapper;
}
@Override
public T selectByKey(Object key) {
//说明:根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
return mapper.selectByPrimaryKey(key);
}
@Override
public int save(T entity) {
//说明:保存一个实体,null的属性也会保存,不会使用数据库默认值
return mapper.insert(entity);
}
@Override
public int delete(Object key) {
//说明:根据主键字段进行删除,方法参数必须包含完整的主键属性
return mapper.deleteByPrimaryKey(key);
}
@Override
public int update(T entity) {
//根据主键更新属性不为null的值
return mapper.updateByPrimaryKeySelective(entity);
}
}
以后再为实体操作类封装服务接口时,可以这么做:继承BaseService,例如CourseService:
import com.example.dao.model.Course;
import com.example.util.Page;
import java.util.List;
import java.util.Map;
public interface CourseService extends BaseService<Course> {
int deleteByIds(String[] ids);
int deleteByIds(Long[] ids);
List<Course> queryList(Map<String, Object> params);
List<Course> queryList(Page<?> page);
}
该服务承诺:除了提供增删改查基础接口功能之外,还提供了批量删除、分页查询的接口,而且是多种参数形式的接口,比较灵活。
然后再创建一个服务接口的实现类CourseServiceImpl:
import com.example.dao.mapper.CourseMapper;
import com.example.dao.model.Course;
import com.example.util.Page;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
*/
@Service
public class CourseServiceImpl extends BaseServiceImpl<Course> implements CourseService {
@Autowired
CourseMapper courseMapper;
@Override
public int deleteByIds(String[] ids) {
return this.courseMapper.deleteByIds(ids);
}
@Override
public int deleteByIds(Long[] ids) {
String[] string_ids = new String[ids.length];
for (int i = 0; i < ids.length; i++) {
string_ids[i] = String.valueOf(ids[i]);
}
return this.courseMapper.deleteByIds(string_ids);
}
@Override
public List<Course> queryList(Map<String,Object> params) {
PageHelper.startPage(Integer.parseInt(params.get("page").toString()), Integer.parseInt(params.get("rows").toString()));
return this.courseMapper.queryList(params);
}
@Override
public List<Course> queryList(Page<?> page) {
return courseMapper.queryList(page.getCondition());
}
}
可以发现,CourseServiceImpl无非是代理了CourseMapper(该类是实体操作类),CourseMapper可能只是实现了一些基本的功能,更灵活和多变的功能只不过是由服务层包装了一层而已。
控制层
建议把所有的Controller的类统一放到名为controller的包名下,建议路由映射采用模块化的方式来注册映射,具体可以参考:springboot极简教程005-URL映射
其他推荐
- 配置文件建议使用application.yml,这也是官方的推荐做法
- MyBatis的注解与XML方式,推荐使用注解方式
- 推荐使用gradle编译而不是Maven,即使Maven占据了主流