随着spring4的出现,也为springboot奠定了基础,其实在了解spring4原理与一些扩展的同时,我们也就可以很方便搭建开发环境,而springboot就是使用了4中的一些新特性与功能,将我们搭建的过程进行了记录,同时通过一些特有的检测机制,实现各种环境的自由选择预搭配,将需要配置的功能模块全部优先实现,而作为开发者,需要做的就是选择。
这里用到springboot+jpa+layui来搭建一个后台管理模块,由于jpa是很纯粹的面向对象持久层标准,针对业务中大多是单表的操作非常适合,代码量会得到大量的简化,而且逻辑会比较清晰,同时内置很多接口能够为我们实现几乎所有的功能。
本次主要选用springboot1.x版本,同样是以maven项目为开端,如果是用idea,我们可以直接使用spring项目构建工具完成。
而我们需要引入的依赖主要的起始就两个:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
由于多模块项目中,parent的引入能有一个,所以不通过spring-boot-starter-parent来控制其版本信息,该有如下:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.10.RELEASE</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
之后同样,构建用户的增删改查模块,由实体类、dao、service、web几个层次组成:
由于hibernate属于jpa的一种实现,我们现在使用hibernate ddl语句自动实现数据库建表,但是前提是,表名与字段属性要定义完全,当然如何定义,之前在spring hibernate 中也有用到,完全一样的做法。
比如User实体:
@Data
@Entity
@Table(name = "USER")
public class User {
@Id
@Column(name = "USER_ID",length = 36)
@GenericGenerator(name = "uuid",strategy = "uuid")
@GeneratedValue(generator = "uuid")
private String userId;
@Column(name = "username",length = 24)
private String username;
@Column(name = "password",length = 56)
private String password;
@Column(name = "user_caption",length = 56)
private String userCaption;
@Column(name = "sex",length = 2)
private String sex;
@Column(name = "age",length = 3)
private String age;
@Column(name = "telephone",length = 16)
private String telephone;
@Column(name = "email",length = 56)
private String email;
@Column(name = "address",length = 128)
private String address;
@Column(name = "description",length = 256)
private String description;
}
这里通过lombok插件减少代码量,需要注意的是,注解都是javax.persistence.*中的;
然后直接启动项目,对应的库中就会自动生成相应的表了。需要注意的是表字段顺序没有按照实体定义的先后,而是按照名称进行排序的。
下一步就是如何构建dao以及相关的服务了,其实在jpa中内置了很多接口,我们可以根据不同需要来进行实现,达到功能扩展的目的。
目前可用的接口有如下:
CrudRepository:实现基础的增删改查
PagingAndSortingRepository:对上一个接口的加强,实现分页排序
JpaRepository:对上一个接口的加强,同时添加excmole查询
JpaSpecificationExecutor:通过Criteria动态查询
Repository:标志接口
每一个接口都有相应的方法与使用场景,具体用到在看,目前按照我们的需要,只用继承JpaRepository就能完成基础的功能,代码很简单:
@Repository
public interface UserDao extends JpaRepository<User,String> {
}
具体的实现就不用我们再写了,这个已经由框架内部实现了,就好比mybatis定义接口而不用管实现,虽然实现方式上有些不同,但原理一样。
接下来就要写service层的逻辑了,由于对单表的操作无非就那几种,我们何不把这些操作全部抽象成共有方法,那样只用做简单的继承就可以实现90%的功能,不用重复做一些无意义的事。
所以构建一个BaseService:
public interface BaseService<R ,T> {
T getById(Serializable id);
T getOne(String property,Object value);
List<T> getAll(Collection<Condition> conditions);
List<T> getAll(T t);
Pager<T> getPager(Pager pager,Collection<Condition> conditions,Collection<Order> orders);
T save(T t);
void saveBatch(Collection<T> ts);
T updateById(T t);
T saveOrUpdate(T t);
void deleteById(Serializable id);
void delete(String property,Object value);
void delete(T t);
void deleteAll(Collection<T> ts);
boolean exist(Serializable id);
boolean exist(T t);
}
那么如何实现这个service,并且作为公共服务可以实现代码复用,那么必须药用到一个东西,就是泛型,可以先看下实现如写的:
public abstract class BaseServiceImpl<R extends Repository<T,Serializable>,T> implements BaseService<R,T>{
/**
* 通过examle查询
*/
protected JpaRepository<T,Serializable> repository;
/**
* 通过Specification查询
*/
protected JpaSpecificationExecutor<T> specificationExecutor;
protected abstract Class<T> getDomainClazz();
@Resource
public void setRepository(R r) {
if(r instanceof JpaRepository){
this.repository = (JpaRepository<T, Serializable>) r;
}
if(r instanceof JpaSpecificationExecutor){
this.specificationExecutor = (JpaSpecificationExecutor<T>) r;
}
}
@Override
public T getById(Serializable id) {
return repository.getOne(id);
}
@Override
public T getOne(String property, Object value) {
return repository.findOne(ConditionHelper.buildExample(getDomainClazz(),property,value));
}
@Override
public List<T> getAll(Collection<Condition> conditions) {
return specificationExecutor.findAll(ConditionHelper.buildSpecification(conditions));
}
@Override
public List<T> getAll(T t) {
return repository.findAll(Example.of(t));
}
@Override
public Pager<T> getPager(Pager pager, Collection<Condition> conditions, Collection<Order> orders) {
Pageable pageable = new PageRequest(pager.getPageIndex(),pager.getPageSize(),ConditionHelper.buildSort(orders));
specificationExecutor.findAll(ConditionHelper.buildSpecification(conditions),pageable);
return null;
}
@Override
public T save(T t) {
return repository.save(t);
}
@Override
public void saveBatch(Collection<T> ts) {
repository.save(ts);
}
@Override
public T updateById(T t) {
return repository.save(t);
}
@Override
public T saveOrUpdate(T t) {
return repository.save(t);
}
@Override
public void deleteById(Serializable id) {
repository.delete(id);
}
@Override
public void delete(String property, Object value) {
}
@Override
public void delete(T t) {
repository.delete(t);
}
@Override
public void deleteAll(Collection<T> ts) {
repository.deleteInBatch(ts);
}
@Override
public boolean exist(Serializable id) {
return repository.exists(id);
}
@Override
public boolean exist(T t) {
return repository.exists(Example.of(t));
}
}
可以看到,所有方法都是由Repository这个接口的子接口完成,具体实现有哪些上面提到过,现在主要使用,JpaRepository、JpaSpecificationExecutor,在dao中我们其实已经实现了这两个接口,那么为什么是这两个接口,因为JpaRepository包含了基本所有功能,而JpaSpecificationExecutor帮助我们更好的扩展功能。
其实在实现的过程中已经做了一些处理,比如分页查询、条件、排序相关参数的处理,这个处理方法因人而异,在这边主要是将查询条件封装成约定的对象,相关查询是基于此对象,然后如何构建,都是一套完整与匹配的构建过程。
其实到这里已经完成了出多了,现在在定义一个controller,同样作为基类共业务服务继承。
public class BaseController<S extends BaseService<?,T>,T> {
@Autowired
protected S s;
@GetMapping("/{id}")
public T get(@PathVariable String id){
return s.getById(id);
}
@GetMapping
public List<T> getAll(Collection<Condition> conditions){
return s.getAll(conditions);
}
@GetMapping(params = {"pageIndex","pageSize"})
public Pager<T> getPager(Pager pager, Collection<Condition> conditions, Collection<Order> orders){
return s.getPager(pager,conditions,orders);
}
@PostMapping
public T saveOrUpdate(T t){
return s.saveOrUpdate(t);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable String id){
s.deleteById(id);
}
}
之后就可以快速完成基础业务功能。
剩下还有很多模块没有完成,当然还有页面,之后会使用layui来实现。
后台代码:https://github.com/suspring/springboot-jpa-ms.git
下次将会在此基础上进行补充完善。