SpringBoot使用JPA实现通用接口(增删查等)

一、前言

在我们的WEB项目中,会写很多很多接口,对于增删改的接口来说,都大致相同。所以在我们的项目中就可以使用通用接口的形式实现增删改。

最终实现是以Entity EntityManager对数据库进行操作。

这里使用SpringBoot JPA 实现通用接口的 删除 功能。其它 Spring等项目思路大致相同。

其它 :

我把实体类、SQL、部分说明等放在了最后。

这里还要参考我的上一篇文章SpringBoot Jpa实体继承通用属性

二、BaseController实现

BaseController 是我们项目中实现通用接口的基础类。

ServiceRepository 都省略了接口类,测试的话自己写一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* BaseController
* 基础的通用接口
*
* @author maxzhao
* @date 2019-08-15 10:33
*/
public class BaseController<T extends BaseEntity, ID> {

@Resource(name = "baseService")
private BaseService<T, ID> baseService;
/**
* 这里是为了让继承此类的接口类,能传入当前接口操作的实体,然后通过 Service 传入 repository
* 传入到 repository 之后 就可以使用 EntityManager 的通用方法了
*
* @param domainClass
*/
public void setDomainClass(Class<T> domainClass) {
baseService.setDomainClass(domainClass);
}

@ApiOperation(value = "/{id}", notes = "逻辑删除", response = ResultObj.class, httpMethod = "DELETE")
@DeleteMapping(value = "{id}")
@ResponseBody
public ResultObj<String> del(@PathVariable(value = "id") ID id) {
return baseService.del(id);
}
}

Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Slf4j
@Service(value = "baseService")
public class BaseServiceImpl<T extends BaseEntity, ID> implements BaseService<T, ID> {
@Autowired
private BaseRepository<T, ID> baseRepository;
/**
* 这里是 domainClass 的跳板
*
* @param domainClass
*/
@Override
public void setDomainClass(Class<T> domainClass) {
baseRepository.setDomainClass(domainClass);
}

@Override
public ResultObj<String> del(ID id) {
baseRepository.del(id);//这里到 repo 中模拟操作
return ResultObj.getDefaultResponse("删除成功");
}
}

Repository

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

/**
* BaseRepositoryImpl
*
* @author maxzhao
* @date 2019-08-15 11:11
*/
@Repository(value = "baseRepository")
@Transactional(readOnly = true)
public class BaseRepositoryImpl<T extends BaseEntity, ID> implements BaseRepository<T, ID> {
@PersistenceContext
private EntityManager entityManager;
/**
* 某个接口的实体类的类型
*/
private Class<T> domainClass;

@Override
public void setDomainClass(Class<T> domainClass) {
this.domainClass = domainClass;
}

@Override
public Boolean del(ID id) {
T entity = entityManager.find(domainClass, id);
return true;
}
}

至此,一个简单的、通用的删除接口就完成了。

删除是比较简单的,因为我做的项目一般都是逻辑删除,所有必须要自定义删除,而且想通用,就需要通用的实体属性,所以也就有了BaseEntity

BaseEntity 解读

这个类就是我前言中提到的通用实体属性的父类,这样就可以统一实体的通用属性,比如主键、逻辑删除状态、添加时间等。

@MappedSuperclass 实体继承映射注解要加载实体的父类上,比如如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* BaseEntity
* 实体继承映射类基础,保存实体的通用属性
*
* @author maxzhao
* @date 2019-08-15 09:39
*/
@Data
@Accessors(chain = true)
@MappedSuperclass//实体继承映射
public class BaseEntity implements Serializable {
@Id
@Column(name = "ID")
@ApiModelProperty(value = "主键")
private Long id;

@Basic
@Column(name = "DEL_STATUS")
@ApiModelProperty(value = "状态 1启用 0 停用")
private Integer delStatus;
}

@DynamicInsert 注解

@DynamicInsert 在实体类的父类上时,是不会对子类的属性产生效果的,对父类自己的属性也没有效果。

其它代码

接口

1
2
3
4
5
6
7
8
9
10
11
@Api(value = "应用接口日志")
@RestController
@RequestMapping("appLogApi")
public class AppLogApiController extends BaseController<AppLogApi, Long> {

@PostConstruct
public void PostConstruct() {
// 重点在这里,这是我能想出的比较简单的实现方法
super.setDomainClass(AppLogApi.class);
}
}

实体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 应用接口日志
*
* @author author
* @date 2019-7-23 15:38:57
*/
@Accessors(chain = true)
@Data
@Entity
@Table(name = "app_log_api", schema = "", catalog = "")
@ApiModel(value = "应用接口日志", description = "应用接口日志")
public class AppLogApi extends BaseEntity implements Serializable {
private static final long serialVersionUID = -1L;

@Basic
@Column(name = "API")
@ApiModelProperty(value = "请求地址")
private String api;
/******/
public AppLogApi() {
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* BaseEntity
* 实体集成映射类基础,保存实体的通用属性
*
* @author maxzhao
* @date 2019-08-15 09:39
*/
@Data
@Accessors(chain = true)
@MappedSuperclass//实体集成映射
public class BaseEntity implements Serializable {
@Id
@Column(name = "ID")
@ApiModelProperty(value = "主键")
private Long id;

@Basic
@Column(name = "DEL_STATUS")
@ApiModelProperty(value = "状态 1启用 0 停用")
private Integer delStatus;
}
1
2
3
4
5
6
7
8
create table gt_boot.app_log_api
(
id bigint(64) not null comment '主键'
primary key,
api varchar(100) null comment '请求地址',
del_status integer null
)
comment '应用接口日志';

@PostConstruct解读

从Java EE5规范开始,Servlet中增加了两个影响Servlet生命周期的注解:

  • @PostConstruct
  • @PreDestroy

修饰一个非静态的void()方法类似与init() destory()类似。写法有如下两种方式:

1
2
3
4
@PostConstruct
public void doA(){}

public @PostConstruct void doB(){}

加载顺序

servlet加载顺序

与本文联系起来说

想要把实体AppLogApi.class传参到BaseRepositoryImpl中,就需要初始化bean之后才可以调用bean,但是@Autowired注入是发生在A的构造方法执行完之后的。

所以要在生成对象时初始化AppLogApi.class参数,就不能在构造函数中实现,这时候@PostContruct的作用就体现出来了, @PostContruct 会在依赖注入完成后被自动调用。

Constructor > @Autowired > @PostConstruct

本文地址: SpringBoot使用JPA实现通用接口(增删查等)

推荐
SpringBoot Jpa实体继承通用属性
IDEA好用的插件
JAVA自定义注解

本文地址: https://github.com/maxzhao-it/blog/post/31330/