refactor(database): 合并数据库模块,简化包结构
This commit is contained in:
@@ -9,12 +9,12 @@
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-boot-service-template-database-jpa</artifactId>
|
||||
<artifactId>spring-boot-service-template-database</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.lanyuanxiaoyao</groupId>
|
||||
<artifactId>spring-boot-service-template-database-common</artifactId>
|
||||
<artifactId>spring-boot-service-template-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -35,16 +35,13 @@
|
||||
<groupId>io.github.openfeign.querydsl</groupId>
|
||||
<artifactId>querydsl-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate.orm</groupId>
|
||||
<artifactId>hibernate-ant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.lanyuanxiaoyao</groupId>
|
||||
<artifactId>spring-boot-service-template-database-common-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -87,6 +84,11 @@
|
||||
<artifactId>jakarta.persistence-api</artifactId>
|
||||
<version>3.2.0</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs>
|
||||
<arg>-Aquerydsl.entityAccessors=true</arg>
|
||||
@@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.lanyuanxiaoyao</groupId>
|
||||
<artifactId>spring-boot-service-template</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-boot-service-template-database-common-test</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.gavlyukovskiy</groupId>
|
||||
<artifactId>p6spy-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,214 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.test;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
|
||||
@Slf4j
|
||||
public class AbstractTestApplication {
|
||||
private static final String BASE_URL = "http://localhost:2490";
|
||||
private static final RestTemplate REST_CLIENT = new RestTemplate();
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
private static final Random random = new Random();
|
||||
private static final String randomChars = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
|
||||
|
||||
protected void testCrud() {
|
||||
formatLog("Save");
|
||||
var cid1 = saveItem("company", randomCompany("Apple")).get("data").asLong();
|
||||
var cid2 = saveItem("company", randomCompany()).get("data").asLong();
|
||||
var cid3 = saveItem("company", randomCompany()).get("data").asLong();
|
||||
|
||||
formatLog("List");
|
||||
assertListItems(listItems("company"), 3, 3);
|
||||
|
||||
formatLog("Detail");
|
||||
var company1 = detailItem("company", cid1);
|
||||
Assert.isTrue(cid1 == company1.at("/data/id").asLong(), "id错误");
|
||||
Assert.isTrue(company1.at("/data/name").asString("").startsWith("Apple"), "name错误");
|
||||
|
||||
var company2 = detailItem("company", cid2);
|
||||
Assert.isTrue(cid2 == company2.at("/data/id").asLong(), "id错误");
|
||||
|
||||
var company3 = detailItem("company", cid3);
|
||||
Assert.isTrue(cid3 == company3.at("/data/id").asLong(), "id错误");
|
||||
|
||||
formatLog("List Page");
|
||||
// language=JSON
|
||||
var pageRequest = """
|
||||
{
|
||||
"page": {
|
||||
"index": 1,
|
||||
"size": 2
|
||||
}
|
||||
}
|
||||
""";
|
||||
assertListItems(listItems("company", pageRequest), 2, 3);
|
||||
|
||||
formatLog("List Queryable");
|
||||
// language=JSON
|
||||
var queryRequest = """
|
||||
{
|
||||
"query": {
|
||||
"notNullEqual": [
|
||||
"name"
|
||||
],
|
||||
"equal": {
|
||||
"name": "Apple"
|
||||
},
|
||||
"like": {
|
||||
"name": "Appl%"
|
||||
},
|
||||
"contain": {
|
||||
"name": "ple"
|
||||
},
|
||||
"startWith": {
|
||||
"name": "Appl"
|
||||
},
|
||||
"endWith": {
|
||||
"name": "le"
|
||||
},
|
||||
"less": {
|
||||
"members": 100
|
||||
},
|
||||
"greatEqual": {
|
||||
"members": 0,
|
||||
"createdTime": "2025-01-01 00:00:00"
|
||||
},
|
||||
"inside": {
|
||||
"name": [
|
||||
"Apple",
|
||||
"Banana"
|
||||
]
|
||||
},
|
||||
"between": {
|
||||
"members": {
|
||||
"start": 0,
|
||||
"end": 100
|
||||
}
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"index": 1,
|
||||
"size": 2
|
||||
}
|
||||
}
|
||||
""";
|
||||
assertListItems(listItems("company", queryRequest), 1, 1);
|
||||
|
||||
formatLog("Clean");
|
||||
removeItem("company", cid1);
|
||||
assertListItems(listItems("company"), 2, 2);
|
||||
removeItem("company", cid2);
|
||||
removeItem("company", cid3);
|
||||
assertListItems(listItems("company"), 0, 0);
|
||||
}
|
||||
|
||||
protected void assertListItems(JsonNode node, int itemSizeTarget, int itemTotalTarget) {
|
||||
var itemSize = node.at("/data/items").size();
|
||||
var itemTotal = node.at("/data/total").asLong();
|
||||
Assert.isTrue(itemSize == itemSizeTarget, "数量错误 (%d)".formatted(itemSize));
|
||||
Assert.isTrue(itemTotal == itemTotalTarget, "分页总数错误 (%d)".formatted(itemTotal));
|
||||
}
|
||||
|
||||
protected void formatLog(String text) {
|
||||
log.info("===== {} =====", text);
|
||||
}
|
||||
|
||||
protected Map<String, Object> randomCompany() {
|
||||
return randomCompany(randomString(10));
|
||||
}
|
||||
|
||||
protected Map<String, Object> randomCompany(String name) {
|
||||
return Map.of(
|
||||
"name", name,
|
||||
"members", randomInt(100)
|
||||
);
|
||||
}
|
||||
|
||||
protected String randomString(String prefix, int length) {
|
||||
return prefix + randomString(length);
|
||||
}
|
||||
|
||||
protected String randomString(int length) {
|
||||
var builder = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
builder.append(randomChars.charAt(random.nextInt(randomChars.length())));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
protected String randomChar(String base) {
|
||||
return base.charAt(randomInt(base.length())) + "";
|
||||
}
|
||||
|
||||
protected int randomInt(int bound) {
|
||||
return random.nextInt(1, bound);
|
||||
}
|
||||
|
||||
protected double randomDouble(int bound) {
|
||||
return random.nextDouble(1, bound);
|
||||
}
|
||||
|
||||
protected HttpHeaders headers() {
|
||||
var headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
return headers;
|
||||
}
|
||||
|
||||
protected JsonNode saveItem(String path, Object body) {
|
||||
var response = REST_CLIENT.postForEntity(
|
||||
"%s/%s/save".formatted(BASE_URL, path),
|
||||
new HttpEntity<>(body, headers()),
|
||||
String.class
|
||||
);
|
||||
Assert.isTrue(response.getStatusCode().is2xxSuccessful(), "请求失败");
|
||||
Assert.notNull(response.getBody(), "请求失败");
|
||||
return MAPPER.readTree(response.getBody());
|
||||
}
|
||||
|
||||
protected JsonNode listItems(String path) {
|
||||
var response = REST_CLIENT.getForEntity(
|
||||
"%s/%s/list".formatted(BASE_URL, path),
|
||||
String.class
|
||||
);
|
||||
Assert.isTrue(response.getStatusCode().is2xxSuccessful(), "请求失败");
|
||||
Assert.notNull(response.getBody(), "请求失败");
|
||||
return MAPPER.readTree(response.getBody());
|
||||
}
|
||||
|
||||
protected JsonNode listItems(String path, String query) {
|
||||
var response = REST_CLIENT.postForEntity(
|
||||
"%s/%s/list".formatted(BASE_URL, path),
|
||||
new HttpEntity<>(query, headers()),
|
||||
String.class
|
||||
);
|
||||
Assert.isTrue(response.getStatusCode().is2xxSuccessful(), "请求失败");
|
||||
Assert.notNull(response.getBody(), "请求失败");
|
||||
return MAPPER.readTree(response.getBody());
|
||||
}
|
||||
|
||||
protected JsonNode detailItem(String path, Long id) {
|
||||
var response = REST_CLIENT.getForEntity(
|
||||
"%s/%s/detail/%d".formatted(BASE_URL, path, id),
|
||||
String.class
|
||||
);
|
||||
Assert.isTrue(response.getStatusCode().is2xxSuccessful(), "请求失败");
|
||||
Assert.notNull(response.getBody(), "请求失败");
|
||||
return MAPPER.readTree(response.getBody());
|
||||
}
|
||||
|
||||
protected void removeItem(String path, Long id) {
|
||||
var response = REST_CLIENT.getForEntity(
|
||||
"%s/%s/remove/%d".formatted(BASE_URL, path, id),
|
||||
Void.class
|
||||
);
|
||||
Assert.isTrue(response.getStatusCode().is2xxSuccessful(), "请求失败");
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
server:
|
||||
port: 2490
|
||||
decorator:
|
||||
datasource:
|
||||
p6spy:
|
||||
multiline: false
|
||||
exclude-categories:
|
||||
- commit
|
||||
- result
|
||||
- resultset
|
||||
- rollback
|
||||
log-format: "%(category)|%(executionTime)|%(sqlSingleLine)"
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.lanyuanxiaoyao</groupId>
|
||||
<artifactId>spring-boot-service-template</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-boot-service-template-database-common</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.lanyuanxiaoyao</groupId>
|
||||
<artifactId>spring-boot-service-template-common</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,71 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.lanyuanxiaoyao</groupId>
|
||||
<artifactId>spring-boot-service-template</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-boot-service-template-database-eq</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.lanyuanxiaoyao</groupId>
|
||||
<artifactId>spring-boot-service-template-database-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.easy-query</groupId>
|
||||
<artifactId>sql-springboot4-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.lanyuanxiaoyao</groupId>
|
||||
<artifactId>spring-boot-service-template-database-common-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>com.easy-query</groupId>
|
||||
<artifactId>sql-processor</artifactId>
|
||||
<version>${easy-query.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,209 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.eq.controller;
|
||||
|
||||
import com.easy.query.core.proxy.AbstractProxyEntity;
|
||||
import com.easy.query.core.proxy.ProxyEntityAvailable;
|
||||
import com.lanyuanxiaoyao.service.template.common.helper.ObjectHelper;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.controller.SimpleController;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.GlobalResponse;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.Query;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.entity.SimpleEntity;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.service.SimpleServiceSupport;
|
||||
import java.util.function.Function;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
/**
|
||||
* 简单控制器支持类,提供基础的CRUD操作实现
|
||||
* <p>
|
||||
* 该类实现了基本的增删改查功能,通过泛型支持不同类型的数据转换。
|
||||
* 子类需要实现对应的Mapper函数来完成实体类与传输对象之间的转换。
|
||||
* </p>
|
||||
*
|
||||
* <h3>设计特点</h3>
|
||||
* <ul>
|
||||
* <li>泛型设计,支持任意实体类型和数据转换</li>
|
||||
* <li>统一的异常处理和事务管理</li>
|
||||
* <li>支持条件查询、分页查询和详情查询</li>
|
||||
* <li>提供抽象的Mapper方法,便于子类实现数据转换逻辑</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>使用说明</h3>
|
||||
* <p>子类需要实现以下抽象方法:</p>
|
||||
* <ul>
|
||||
* <li>saveItemMapper(): 保存项到实体的转换函数</li>
|
||||
* <li>listItemMapper(): 实体到列表项的转换函数</li>
|
||||
* <li>detailItemMapper(): 实体到详情项的转换函数</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <ENTITY> 实体类型,必须继承SimpleEntity
|
||||
* @param <SAVE_ITEM> 保存项类型
|
||||
* @param <LIST_ITEM> 列表项类型
|
||||
* @param <DETAIL_ITEM> 详情项类型
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public abstract class SimpleControllerSupport<ENTITY extends SimpleEntity & ProxyEntityAvailable<ENTITY, PROXY>, PROXY extends AbstractProxyEntity<PROXY, ENTITY>, SAVE_ITEM, LIST_ITEM, DETAIL_ITEM> implements SimpleController<SAVE_ITEM, LIST_ITEM, DETAIL_ITEM> {
|
||||
protected final SimpleServiceSupport<ENTITY, PROXY> service;
|
||||
|
||||
/**
|
||||
* 保存实体对象
|
||||
* <p>
|
||||
* 将保存项转换为实体对象后保存,返回保存后的实体ID。
|
||||
* 支持新增和更新操作,通过事务保证数据一致性。
|
||||
* </p>
|
||||
*
|
||||
* @param item 需要保存的项
|
||||
* @return 返回保存后的实体ID响应对象,格式:{status: 0, message: "OK", data: 实体ID}
|
||||
* @throws Exception 保存过程中可能抛出的异常
|
||||
*/
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
@PostMapping(SAVE)
|
||||
@Override
|
||||
public GlobalResponse<Long> save(@RequestBody SAVE_ITEM item) throws Exception {
|
||||
var mapper = saveItemMapper();
|
||||
return GlobalResponse.responseSuccess(service.save(mapper.apply(item)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有实体列表
|
||||
* <p>
|
||||
* 查询所有记录,不带任何过滤条件,返回分页格式的数据。
|
||||
* 将实体对象转换为列表项对象后返回。
|
||||
* </p>
|
||||
*
|
||||
* @return 返回实体列表响应对象,格式:{status: 0, message: "OK", data: {items: [...], total: total}}
|
||||
* @throws Exception 查询过程中可能抛出的异常
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@GetMapping(LIST)
|
||||
@Override
|
||||
public GlobalResponse<GlobalResponse.ListItem<LIST_ITEM>> list() throws Exception {
|
||||
var mapper = listItemMapper();
|
||||
var result = service.list();
|
||||
return GlobalResponse.responseListData(
|
||||
result
|
||||
.stream()
|
||||
.map(entity -> {
|
||||
try {
|
||||
return mapper.apply(entity);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.toList(),
|
||||
result.size()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据查询条件获取实体列表
|
||||
* <p>
|
||||
* 支持复杂的查询条件、排序和分页,返回符合条件的数据。
|
||||
* 将实体对象转换为列表项对象后返回。
|
||||
* </p>
|
||||
*
|
||||
* @param query 查询条件对象,包含过滤条件、排序规则和分页信息
|
||||
* @return 返回符合条件的实体列表响应对象,格式:{status: 0, message: "OK", data: {items: [...], total: total}}
|
||||
* @throws Exception 查询过程中可能抛出的异常
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@PostMapping(LIST)
|
||||
@Override
|
||||
public GlobalResponse<GlobalResponse.ListItem<LIST_ITEM>> list(@RequestBody Query query) throws Exception {
|
||||
if (ObjectHelper.isNull(query)) {
|
||||
return GlobalResponse.responseListData();
|
||||
}
|
||||
var mapper = listItemMapper();
|
||||
var result = service.list(query);
|
||||
return GlobalResponse.responseListData(
|
||||
result.items()
|
||||
.stream()
|
||||
.map(entity -> {
|
||||
try {
|
||||
return mapper.apply(entity);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.toList(),
|
||||
result.total()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取实体详情
|
||||
* <p>
|
||||
* 根据主键ID查询单条记录的详细信息,转换为详情项对象后返回。
|
||||
* 如果记录不存在则抛出异常。
|
||||
* </p>
|
||||
*
|
||||
* @param id 实体主键ID
|
||||
* @return 返回实体详情响应对象,格式:{status: 0, message: "OK", data: 详情数据}
|
||||
* @throws Exception 查询过程中可能抛出的异常
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@GetMapping(DETAIL)
|
||||
@Override
|
||||
public GlobalResponse<DETAIL_ITEM> detail(@PathVariable("id") Long id) throws Exception {
|
||||
var mapper = detailItemMapper();
|
||||
return GlobalResponse.responseSuccess(mapper.apply(service.detailOrThrow(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID删除实体对象
|
||||
* <p>
|
||||
* 根据主键ID删除指定的记录,执行成功后返回成功响应。
|
||||
* 通过事务保证删除操作的一致性。
|
||||
* </p>
|
||||
*
|
||||
* @param id 需要删除的实体主键ID
|
||||
* @return 返回删除结果响应对象,格式:{status: 0, message: "OK", data: null}
|
||||
* @throws Exception 删除过程中可能抛出的异常
|
||||
*/
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
@GetMapping(REMOVE)
|
||||
@Override
|
||||
public GlobalResponse<Object> remove(@PathVariable("id") Long id) throws Exception {
|
||||
service.remove(id);
|
||||
return GlobalResponse.responseSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存项映射器,将保存项转换为实体对象
|
||||
* <p>
|
||||
* 子类需要实现此方法,定义保存项到实体的转换逻辑。
|
||||
* </p>
|
||||
*
|
||||
* @return Function<SAVE_ITEM, ENTITY> 保存项到实体的转换函数
|
||||
*/
|
||||
protected abstract Function<SAVE_ITEM, ENTITY> saveItemMapper();
|
||||
|
||||
/**
|
||||
* 列表项映射器,将实体对象转换为列表项
|
||||
* <p>
|
||||
* 子类需要实现此方法,定义实体到列表项的转换逻辑。
|
||||
* </p>
|
||||
*
|
||||
* @return Function<ENTITY, LIST_ITEM> 实体到列表项的转换函数
|
||||
*/
|
||||
protected abstract Function<ENTITY, LIST_ITEM> listItemMapper();
|
||||
|
||||
/**
|
||||
* 详情项映射器,将实体对象转换为详情项
|
||||
* <p>
|
||||
* 子类需要实现此方法,定义实体到详情项的转换逻辑。
|
||||
* </p>
|
||||
*
|
||||
* @return Function<ENTITY, DETAIL_ITEM> 实体到详情项的转换函数
|
||||
*/
|
||||
protected abstract Function<ENTITY, DETAIL_ITEM> detailItemMapper();
|
||||
|
||||
public interface Mapper<S, T> {
|
||||
T map(S source) throws Exception;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.eq.entity;
|
||||
|
||||
import com.easy.query.core.annotation.Column;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@FieldNameConstants
|
||||
public class IdOnlyEntity {
|
||||
@Column(primaryKey = true, primaryKeyGenerator = SnowflakeIdGenerator.class)
|
||||
private Long id;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.eq.entity;
|
||||
|
||||
import com.easy.query.core.annotation.LogicDelete;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@FieldNameConstants
|
||||
public class LogicDeleteEntity extends IdOnlyEntity {
|
||||
@LogicDelete
|
||||
private Boolean deleted = false;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.eq.entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString(callSuper = true)
|
||||
@FieldNameConstants
|
||||
public class SimpleEntity extends LogicDeleteEntity {
|
||||
private LocalDateTime createdTime;
|
||||
private LocalDateTime modifiedTime;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.eq.entity;
|
||||
|
||||
import com.easy.query.core.basic.extension.generated.PrimaryKeyGenerator;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.helper.SnowflakeHelper;
|
||||
import java.io.Serializable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SnowflakeIdGenerator implements PrimaryKeyGenerator {
|
||||
@Override
|
||||
public Serializable getPrimaryKey() {
|
||||
return SnowflakeHelper.next();
|
||||
}
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.eq.service;
|
||||
|
||||
import com.easy.query.api.proxy.client.EasyEntityQuery;
|
||||
import com.easy.query.core.enums.SQLExecuteStrategyEnum;
|
||||
import com.easy.query.core.proxy.AbstractProxyEntity;
|
||||
import com.easy.query.core.proxy.ProxyEntityAvailable;
|
||||
import com.lanyuanxiaoyao.service.template.common.helper.ObjectHelper;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.Page;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.Query;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.exception.IdNotFoundException;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.service.QueryParser;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.service.SimpleService;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.entity.SimpleEntity;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.mapstruct.Named;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public abstract class SimpleServiceSupport<ENTITY extends SimpleEntity & ProxyEntityAvailable<ENTITY, PROXY>, PROXY extends AbstractProxyEntity<PROXY, ENTITY>> implements SimpleService<ENTITY> {
|
||||
private static final int DEFAULT_PAGE_INDEX = 1;
|
||||
private static final int DEFAULT_PAGE_SIZE = 10;
|
||||
|
||||
protected final EasyEntityQuery entityQuery;
|
||||
private final Class<ENTITY> target;
|
||||
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
@Override
|
||||
public Long save(ENTITY entity) {
|
||||
if (ObjectHelper.isNull(entity.getId())) {
|
||||
entityQuery.insertable(entity).executeRows();
|
||||
} else {
|
||||
entityQuery.updatable(entity)
|
||||
.setSQLStrategy(SQLExecuteStrategyEnum.ONLY_NOT_NULL_COLUMNS)
|
||||
.executeRows();
|
||||
}
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
@Override
|
||||
public void save(Iterable<ENTITY> entities) {
|
||||
var insertList = new ArrayList<ENTITY>();
|
||||
var updateList = new ArrayList<ENTITY>();
|
||||
for (var entity : entities) {
|
||||
if (ObjectHelper.isNull(entity.getId())) {
|
||||
insertList.add(entity);
|
||||
} else {
|
||||
updateList.add(entity);
|
||||
}
|
||||
}
|
||||
if (ObjectHelper.isNotEmpty(insertList)) {
|
||||
entityQuery.insertable(insertList).executeRows();
|
||||
}
|
||||
if (ObjectHelper.isNotEmpty(updateList)) {
|
||||
entityQuery.updatable(updateList)
|
||||
.setSQLStrategy(SQLExecuteStrategyEnum.ONLY_NOT_NULL_COLUMNS)
|
||||
.executeRows();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
@Override
|
||||
public Long count() {
|
||||
return entityQuery.queryable(target).count();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
@Override
|
||||
public List<ENTITY> list() {
|
||||
return entityQuery.queryable(target).toList();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
@Override
|
||||
public List<ENTITY> list(Set<Long> ids) {
|
||||
if (ObjectHelper.isEmpty(ids)) {
|
||||
return List.of();
|
||||
}
|
||||
return entityQuery.queryable(target)
|
||||
.whereByIds(ids)
|
||||
.toList();
|
||||
}
|
||||
|
||||
protected void commonPredicates(PROXY proxy) {
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
@Override
|
||||
public Page<ENTITY> list(Query query) {
|
||||
var index = DEFAULT_PAGE_INDEX;
|
||||
var size = DEFAULT_PAGE_SIZE;
|
||||
if (ObjectHelper.isNotNull(query.page())) {
|
||||
index = Math.max(ObjectHelper.defaultIfNull(query.page().index(), DEFAULT_PAGE_INDEX), 1);
|
||||
size = Math.max(ObjectHelper.defaultIfNull(query.page().size(), DEFAULT_PAGE_SIZE), 1);
|
||||
}
|
||||
|
||||
var result = entityQuery.queryable(target)
|
||||
.where(this::commonPredicates)
|
||||
.where(proxy -> new EqQueryParser<ENTITY, PROXY>(query.query(), proxy).build())
|
||||
.orderBy(ObjectHelper.isNotEmpty(query.sort()), proxy -> query.sort().forEach(sort -> proxy.anyColumn(sort.column()).orderBy(Query.Sortable.Direction.ASC.equals(sort.direction()))))
|
||||
.toPageResult(index, size);
|
||||
|
||||
return new Page<>(result.getData(), result.getTotal());
|
||||
}
|
||||
|
||||
private Optional<ENTITY> detailOptional(Long id) {
|
||||
if (ObjectHelper.isNull(id)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return entityQuery.queryable(target)
|
||||
.whereById(id)
|
||||
.singleOptional();
|
||||
}
|
||||
|
||||
@Named("detail")
|
||||
@Transactional(readOnly = true)
|
||||
@Override
|
||||
public ENTITY detail(Long id) {
|
||||
return detailOptional(id).orElse(null);
|
||||
}
|
||||
|
||||
@Named("detailOrThrow")
|
||||
@Transactional(readOnly = true)
|
||||
@Override
|
||||
public ENTITY detailOrThrow(Long id) {
|
||||
return detailOptional(id).orElseThrow(() -> new IdNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
@Override
|
||||
public void remove(Long id) {
|
||||
if (ObjectHelper.isNotNull(id)) {
|
||||
entityQuery.deletable(target)
|
||||
.whereById(id)
|
||||
.allowDeleteStatement(true)
|
||||
.executeRows();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
@Override
|
||||
public void remove(Set<Long> ids) {
|
||||
if (ObjectHelper.isNotEmpty(ids)) {
|
||||
entityQuery.deletable(target)
|
||||
.whereByIds(ids)
|
||||
.allowDeleteStatement(true)
|
||||
.executeRows();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class EqQueryParser<ENTITY extends SimpleEntity & ProxyEntityAvailable<ENTITY, PROXY>, PROXY extends AbstractProxyEntity<PROXY, ENTITY>> extends QueryParser<PROXY> {
|
||||
public EqQueryParser(Query.Queryable queryable, PROXY container) {
|
||||
super(queryable, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void nullEqual(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.nullEqual().forEach(column -> proxy.anyColumn(column).isNull());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notNullEqual(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.notNullEqual().forEach(column -> proxy.anyColumn(column).isNotNull());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void empty(Query.Queryable queryable, PROXY proxy) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notEmpty(Query.Queryable queryable, PROXY proxy) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void equal(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.equal().forEach((column, value) -> proxy.anyColumn(column).eq(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notEqual(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.notEqual().forEach((column, value) -> proxy.anyColumn(column).ne(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void like(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.like().forEach((column, value) -> proxy.anyColumn(column).likeRaw(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notLike(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.notLike().forEach((column, value) -> proxy.anyColumn(column).notLikeRaw(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void contain(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.contain().forEach((column, value) -> proxy.anyColumn(column).like(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notContain(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.notContain().forEach((column, value) -> proxy.anyColumn(column).notLike(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startWith(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.startWith().forEach((column, value) -> proxy.anyColumn(column).likeMatchLeft(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notStartWith(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.notStartWith().forEach((column, value) -> proxy.anyColumn(column).notLikeMatchLeft(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void endWith(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.endWith().forEach((column, value) -> proxy.anyColumn(column).likeMatchRight(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notEndWith(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.notEndWith().forEach((column, value) -> proxy.anyColumn(column).notLikeMatchRight(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void great(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.great().forEach((column, value) -> proxy.anyColumn(column).gt(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void less(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.less().forEach((column, value) -> proxy.anyColumn(column).lt(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void greatEqual(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.greatEqual().forEach((column, value) -> proxy.anyColumn(column).ge(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void lessEqual(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.lessEqual().forEach((column, value) -> proxy.anyColumn(column).le(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void inside(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.inside()
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> ObjectHelper.isNotEmpty(entry.getValue()))
|
||||
.forEach(entry -> proxy.anyColumn(entry.getKey()).in(entry.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notInside(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.notInside()
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> ObjectHelper.isNotEmpty(entry.getValue()))
|
||||
.forEach(entry -> proxy.anyColumn(entry.getKey()).notIn(entry.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void between(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.between().forEach((column, value) -> {
|
||||
proxy.anyColumn(column).gt(value.start());
|
||||
proxy.anyColumn(column).le(value.end());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notBetween(Query.Queryable queryable, PROXY proxy) {
|
||||
queryable.between().forEach((column, value) -> {
|
||||
proxy.anyColumn(column).le(value.start());
|
||||
proxy.anyColumn(column).gt(value.end());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
create table if not exists Company
|
||||
(
|
||||
id bigint primary key,
|
||||
name varchar(255) not null,
|
||||
members int not null,
|
||||
created_time timestamp not null default current_timestamp(),
|
||||
modified_time timestamp not null default current_timestamp() on update current_timestamp(),
|
||||
deleted tinyint not null default false
|
||||
);
|
||||
|
||||
create table if not exists Employee
|
||||
(
|
||||
id bigint primary key,
|
||||
name varchar(255) not null,
|
||||
age int not null,
|
||||
company_id bigint not null,
|
||||
created_time timestamp not null default current_timestamp(),
|
||||
modified_time timestamp not null default current_timestamp() on update current_timestamp(),
|
||||
deleted tinyint not null default false
|
||||
);
|
||||
@@ -1,76 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.eq;
|
||||
|
||||
import com.easy.query.api.proxy.client.EasyEntityQuery;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.test.AbstractTestApplication;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.entity.Company;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.entity.Employee;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.entity.proxy.EmployeeProxy;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@SpringBootApplication
|
||||
public class TestApplication extends AbstractTestApplication {
|
||||
private final EasyEntityQuery entityQuery;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TestApplication.class, args);
|
||||
}
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void runTests() {
|
||||
testCrud();
|
||||
testDelete();
|
||||
testQuery();
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private void testDelete() {
|
||||
formatLog("Delete");
|
||||
saveItem("company", randomCompany());
|
||||
saveItem("company", randomCompany());
|
||||
entityQuery.deletable(Company.class)
|
||||
.where(proxy -> proxy.id().isNotNull())
|
||||
.allowDeleteStatement(true)
|
||||
.executeRows();
|
||||
}
|
||||
|
||||
private void testQuery() {
|
||||
formatLog("Added");
|
||||
var company1 = Company.builder().name(randomString(5)).members(randomInt(100)).build();
|
||||
entityQuery.insertable(company1).executeRows();
|
||||
var company2 = Company.builder().name(randomString(5)).members(randomInt(100)).build();
|
||||
entityQuery.insertable(company2).executeRows();
|
||||
var employee1 = Employee.builder().name("Tom").age(randomInt(100)).companyId(company1.getId()).build();
|
||||
entityQuery.insertable(employee1).executeRows();
|
||||
var employee2 = Employee.builder().name(randomString(10)).age(randomInt(100)).companyId(company2.getId()).build();
|
||||
entityQuery.insertable(employee2).executeRows();
|
||||
|
||||
formatLog("Query");
|
||||
var employees1 = entityQuery.queryable(Employee.class)
|
||||
.include(EmployeeProxy::company)
|
||||
.where(proxy -> {
|
||||
proxy.name().isNotNull();
|
||||
proxy.name().eq("Tom");
|
||||
proxy.name().startsWith("To");
|
||||
proxy.name().endsWith("om");
|
||||
proxy.age().lt(200);
|
||||
proxy.age().gt(0);
|
||||
proxy.name().in(List.of("Tom", "Mike"));
|
||||
})
|
||||
.toList();
|
||||
Assert.isTrue(employees1.size() == 1, "查询数量错误");
|
||||
|
||||
formatLog("Clean");
|
||||
entityQuery.deletable(Company.class).where(proxy -> proxy.id().isNotNull()).executeRows();
|
||||
entityQuery.deletable(Employee.class).where(proxy -> proxy.id().isNotNull()).executeRows();
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.eq.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.entity.Company;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.entity.proxy.CompanyProxy;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.service.CompanyService;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.function.Function;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("company")
|
||||
public class CompanyController extends SimpleControllerSupport<Company, CompanyProxy, CompanyController.SaveItem, CompanyController.ListItem, CompanyController.DetailItem> {
|
||||
public CompanyController(CompanyService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<SaveItem, Company> saveItemMapper() {
|
||||
return item -> {
|
||||
var company = new Company();
|
||||
company.setId(item.id());
|
||||
company.setName(item.name());
|
||||
company.setMembers(item.members());
|
||||
return company;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Company, ListItem> listItemMapper() {
|
||||
return company -> new ListItem(
|
||||
company.getId(),
|
||||
company.getName(),
|
||||
company.getMembers()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Company, DetailItem> detailItemMapper() {
|
||||
return company -> new DetailItem(
|
||||
company.getId(),
|
||||
company.getName(),
|
||||
company.getMembers(),
|
||||
company.getCreatedTime(),
|
||||
company.getModifiedTime()
|
||||
);
|
||||
}
|
||||
|
||||
public record SaveItem(
|
||||
Long id,
|
||||
String name,
|
||||
Integer members
|
||||
) {
|
||||
}
|
||||
|
||||
public record ListItem(
|
||||
Long id,
|
||||
String name,
|
||||
Integer members
|
||||
) {
|
||||
}
|
||||
|
||||
public record DetailItem(
|
||||
Long id,
|
||||
String name,
|
||||
Integer members,
|
||||
LocalDateTime createdTime,
|
||||
LocalDateTime modifiedTime
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.eq.entity;
|
||||
|
||||
import com.easy.query.core.annotation.EntityProxy;
|
||||
import com.easy.query.core.annotation.Navigate;
|
||||
import com.easy.query.core.annotation.Table;
|
||||
import com.easy.query.core.enums.RelationTypeEnum;
|
||||
import com.easy.query.core.proxy.ProxyEntityAvailable;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.entity.proxy.CompanyProxy;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString(callSuper = true)
|
||||
@FieldNameConstants
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Table
|
||||
@EntityProxy
|
||||
public class Company extends SimpleEntity implements ProxyEntityAvailable<Company, CompanyProxy> {
|
||||
private String name;
|
||||
private Integer members;
|
||||
|
||||
@Navigate(value = RelationTypeEnum.OneToMany, selfProperty = {"id"}, targetProperty = {"companyId"})
|
||||
private List<Employee> employees;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.eq.entity;
|
||||
|
||||
import com.easy.query.core.annotation.EntityProxy;
|
||||
import com.easy.query.core.annotation.Navigate;
|
||||
import com.easy.query.core.annotation.Table;
|
||||
import com.easy.query.core.enums.RelationTypeEnum;
|
||||
import com.easy.query.core.proxy.ProxyEntityAvailable;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.entity.proxy.EmployeeProxy;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString(callSuper = true)
|
||||
@FieldNameConstants
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Table
|
||||
@EntityProxy
|
||||
public class Employee extends SimpleEntity implements ProxyEntityAvailable<Employee, EmployeeProxy> {
|
||||
private String name;
|
||||
private Integer age;
|
||||
private Long companyId;
|
||||
|
||||
@Navigate(value = RelationTypeEnum.OneToOne, selfProperty = {"companyId"}, targetProperty = {"id"})
|
||||
private Company company;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.eq.service;
|
||||
|
||||
import com.easy.query.api.proxy.client.EasyEntityQuery;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.entity.Company;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.entity.proxy.CompanyProxy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class CompanyService extends SimpleServiceSupport<Company, CompanyProxy> {
|
||||
public CompanyService(EasyEntityQuery entityQuery) {
|
||||
super(entityQuery, Company.class);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.eq.service;
|
||||
|
||||
import com.easy.query.api.proxy.client.EasyEntityQuery;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.entity.Employee;
|
||||
import com.lanyuanxiaoyao.service.template.database.eq.entity.proxy.EmployeeProxy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class EmployeeService extends SimpleServiceSupport<Employee, EmployeeProxy> {
|
||||
public EmployeeService(EasyEntityQuery entityQuery) {
|
||||
super(entityQuery, Employee.class);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
spring:
|
||||
profiles:
|
||||
include: test
|
||||
datasource:
|
||||
url: "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=MySQL;DATABASE_TO_LOWER=TRUE;INIT=runscript from '/Users/lanyuanxiaoyao/Project/IdeaProjects/spring-boot-service-template/spring-boot-service-template-database/spring-boot-service-template-database-eq/src/test/initial.sql'"
|
||||
username: test
|
||||
password: test
|
||||
driver-class-name: org.h2.Driver
|
||||
easy-query:
|
||||
database: mysql
|
||||
name-conversion: underlined
|
||||
print-sql: false
|
||||
print-nav-sql: false
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,70 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Company;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.service.CompanyService;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.function.Function;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("company")
|
||||
public class CompanyController extends SimpleControllerSupport<Company, CompanyController.SaveItem, CompanyController.ListItem, CompanyController.DetailItem> {
|
||||
public CompanyController(CompanyService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<SaveItem, Company> saveItemMapper() {
|
||||
return item -> {
|
||||
var company = new Company();
|
||||
company.setId(item.id());
|
||||
company.setName(item.name());
|
||||
company.setMembers(item.members());
|
||||
return company;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Company, ListItem> listItemMapper() {
|
||||
return company -> new ListItem(
|
||||
company.getId(),
|
||||
company.getName(),
|
||||
company.getMembers()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Company, DetailItem> detailItemMapper() {
|
||||
return company -> new DetailItem(
|
||||
company.getId(),
|
||||
company.getName(),
|
||||
company.getMembers(),
|
||||
company.getCreatedTime(),
|
||||
company.getModifiedTime()
|
||||
);
|
||||
}
|
||||
|
||||
public record SaveItem(
|
||||
Long id,
|
||||
String name,
|
||||
Integer members
|
||||
) {
|
||||
}
|
||||
|
||||
public record ListItem(
|
||||
Long id,
|
||||
String name,
|
||||
Integer members
|
||||
) {
|
||||
}
|
||||
|
||||
public record DetailItem(
|
||||
Long id,
|
||||
String name,
|
||||
Integer members,
|
||||
LocalDateTime createdTime,
|
||||
LocalDateTime modifiedTime
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.service.CompanyService;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.service.EmployeeService;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.function.Function;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("employee")
|
||||
public class EmployeeController extends SimpleControllerSupport<Employee, EmployeeController.SaveItem, EmployeeController.ListItem, EmployeeController.DetailItem> {
|
||||
private final CompanyService companyService;
|
||||
|
||||
public EmployeeController(EmployeeService service, CompanyService companyService) {
|
||||
super(service);
|
||||
this.companyService = companyService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<SaveItem, Employee> saveItemMapper() {
|
||||
return item -> {
|
||||
var employee = new Employee();
|
||||
employee.setId(item.id());
|
||||
employee.setName(item.name());
|
||||
employee.setAge(item.age());
|
||||
employee.setRole(Employee.Role.USER);
|
||||
employee.setCompany(companyService.detailOrThrow(item.companyId()));
|
||||
return employee;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Employee, ListItem> listItemMapper() {
|
||||
return employee -> new ListItem(
|
||||
employee.getId(),
|
||||
employee.getName(),
|
||||
employee.getAge(),
|
||||
employee.getRole()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Employee, DetailItem> detailItemMapper() {
|
||||
return employee -> new DetailItem(
|
||||
employee.getId(),
|
||||
employee.getCompany().getId(),
|
||||
employee.getName(),
|
||||
employee.getAge(),
|
||||
employee.getRole(),
|
||||
employee.getCreatedTime(),
|
||||
employee.getModifiedTime()
|
||||
);
|
||||
}
|
||||
|
||||
public record SaveItem(
|
||||
Long id,
|
||||
Long companyId,
|
||||
String name,
|
||||
Integer age,
|
||||
Employee.Role role
|
||||
) {
|
||||
}
|
||||
|
||||
public record ListItem(
|
||||
Long id,
|
||||
String name,
|
||||
Integer age,
|
||||
Employee.Role role
|
||||
) {
|
||||
}
|
||||
|
||||
public record DetailItem(
|
||||
Long id,
|
||||
Long companyId,
|
||||
String name,
|
||||
Integer age,
|
||||
Employee.Role role,
|
||||
LocalDateTime createdTime,
|
||||
LocalDateTime modifiedTime
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Report;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.service.EmployeeService;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.service.ReportService;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.function.Function;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("report")
|
||||
public class ReportController extends SimpleControllerSupport<Report, ReportController.SaveItem, ReportController.ListItem, ReportController.DetailItem> {
|
||||
private final EmployeeService employeeService;
|
||||
|
||||
public ReportController(ReportService service, EmployeeService employeeService) {
|
||||
super(service);
|
||||
this.employeeService = employeeService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<SaveItem, Report> saveItemMapper() {
|
||||
return item -> {
|
||||
var report = new Report();
|
||||
report.setId(item.id());
|
||||
report.setScore(item.score());
|
||||
report.setLevel(item.level());
|
||||
report.setEmployeeId(item.employeeId());
|
||||
return report;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Report, ListItem> listItemMapper() {
|
||||
return report -> {
|
||||
var employee = employeeService.detailOrThrow(report.getEmployeeId());
|
||||
return new ListItem(
|
||||
report.getId(),
|
||||
employee.getId(),
|
||||
employee.getName(),
|
||||
report.getScore(),
|
||||
report.getLevel()
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Report, DetailItem> detailItemMapper() {
|
||||
return report -> {
|
||||
var employee = employeeService.detailOrThrow(report.getEmployeeId());
|
||||
return new DetailItem(
|
||||
report.getId(),
|
||||
employee.getId(),
|
||||
employee.getName(),
|
||||
report.getScore(),
|
||||
report.getLevel(),
|
||||
report.getCreatedTime(),
|
||||
report.getModifiedTime()
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
public record SaveItem(
|
||||
Long id,
|
||||
Double score,
|
||||
Report.Level level,
|
||||
Long employeeId
|
||||
) {
|
||||
}
|
||||
|
||||
public record ListItem(
|
||||
Long id,
|
||||
Long employeeId,
|
||||
String employeeName,
|
||||
Double score,
|
||||
Report.Level level
|
||||
) {
|
||||
}
|
||||
|
||||
public record DetailItem(
|
||||
Long id,
|
||||
Long employeeId,
|
||||
String employeeName,
|
||||
Double score,
|
||||
Report.Level level,
|
||||
LocalDateTime createdTime,
|
||||
LocalDateTime modifiedTime
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Embeddable
|
||||
@Setter
|
||||
@Getter
|
||||
@ToString
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class Address {
|
||||
@Column(comment = "街道")
|
||||
private String street;
|
||||
|
||||
@Column(comment = "城市")
|
||||
private String city;
|
||||
|
||||
@Column(comment = "省/州")
|
||||
private String state;
|
||||
|
||||
@Column(comment = "邮政编码")
|
||||
private String zipCode;
|
||||
|
||||
@Column(comment = "国家")
|
||||
private String country;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
import org.hibernate.annotations.DynamicInsert;
|
||||
import org.hibernate.annotations.DynamicUpdate;
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@ToString(callSuper = true)
|
||||
@FieldNameConstants
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Entity
|
||||
@SoftDelete
|
||||
@DynamicUpdate
|
||||
@DynamicInsert
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@Table(comment = "企业")
|
||||
public class Company extends SimpleEntity {
|
||||
@Column(nullable = false, comment = "名称")
|
||||
private String name;
|
||||
@Column(nullable = false, comment = "成员数")
|
||||
private Integer members;
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ConstraintMode;
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.Index;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.MapKeyEnumerated;
|
||||
import jakarta.persistence.OrderColumn;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Version;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
import org.hibernate.annotations.DynamicInsert;
|
||||
import org.hibernate.annotations.DynamicUpdate;
|
||||
import org.hibernate.annotations.Formula;
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@ToString(callSuper = true)
|
||||
@FieldNameConstants
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Entity
|
||||
@SoftDelete
|
||||
@DynamicUpdate
|
||||
@DynamicInsert
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@Table(
|
||||
comment = "员工",
|
||||
indexes = {
|
||||
@Index(name = "idx_employee_name", columnList = "name"),
|
||||
@Index(name = "idx_employee_salary", columnList = "salary"),
|
||||
@Index(name = "idx_employee_active", columnList = "active")
|
||||
}
|
||||
)
|
||||
public class Employee extends SimpleEntity {
|
||||
@Column(nullable = false, length = 100, comment = "名称")
|
||||
private String name;
|
||||
|
||||
@Column(nullable = false, comment = "年龄")
|
||||
private Integer age;
|
||||
|
||||
@Column(nullable = false, comment = "角色")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Role role;
|
||||
|
||||
@Column(unique = true, length = 50, comment = "工号")
|
||||
private String code;
|
||||
|
||||
@Column(nullable = false, comment = "薪资")
|
||||
private BigDecimal salary;
|
||||
|
||||
@Column(precision = 19, scale = 4, comment = "奖金")
|
||||
private BigDecimal bonus;
|
||||
|
||||
@Column(comment = "是否激活")
|
||||
@ColumnDefault("true")
|
||||
private Boolean active;
|
||||
|
||||
@Lob
|
||||
@Column(comment = "简历(大文本)")
|
||||
private String resume;
|
||||
|
||||
@Version
|
||||
private Long version;
|
||||
|
||||
@Column(insertable = false, updatable = false)
|
||||
@Formula("salary + COALESCE(bonus, 0)")
|
||||
private BigDecimal earnings;
|
||||
|
||||
@Embedded
|
||||
private Address address;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||
@ToString.Exclude
|
||||
private Company company;
|
||||
|
||||
@ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
|
||||
@JoinTable(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||
@ToString.Exclude
|
||||
@Builder.Default
|
||||
private Set<Skill> skills = new HashSet<>();
|
||||
|
||||
@ElementCollection
|
||||
@JoinTable(joinColumns = @JoinColumn(nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)))
|
||||
@Column(comment = "兴趣")
|
||||
@OrderColumn
|
||||
@ToString.Exclude
|
||||
@Builder.Default
|
||||
private List<String> hobbies = new ArrayList<>();
|
||||
|
||||
@ElementCollection
|
||||
@JoinTable(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||
@Column(comment = "属性")
|
||||
@Builder.Default
|
||||
private Map<String, String> properties = new HashMap<>();
|
||||
|
||||
@ElementCollection
|
||||
@JoinTable(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||
@MapKeyEnumerated(EnumType.STRING)
|
||||
@Column(nullable = false)
|
||||
@Builder.Default
|
||||
private Map<ConnectionType, String> connections = new HashMap<>();
|
||||
|
||||
public enum Role {
|
||||
USER,
|
||||
ADMIN,
|
||||
}
|
||||
|
||||
public enum ConnectionType {
|
||||
EMAIL,
|
||||
PHONE,
|
||||
ADDRESS,
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
import org.hibernate.annotations.DynamicInsert;
|
||||
import org.hibernate.annotations.DynamicUpdate;
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@ToString(callSuper = true)
|
||||
@FieldNameConstants
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Entity
|
||||
@SoftDelete
|
||||
@DynamicUpdate
|
||||
@DynamicInsert
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@Table(comment = "报告")
|
||||
public class Report extends SimpleEntity {
|
||||
@Column(nullable = false, comment = "分数")
|
||||
@Builder.Default
|
||||
private Double score = 0.0;
|
||||
@Column(nullable = false, comment = "等级")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Level level;
|
||||
|
||||
@Column(nullable = false, comment = "员工 ID")
|
||||
private Long employeeId;
|
||||
|
||||
public enum Level {
|
||||
A, B, C, D, E
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
import org.hibernate.annotations.DynamicInsert;
|
||||
import org.hibernate.annotations.DynamicUpdate;
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@ToString(callSuper = true)
|
||||
@FieldNameConstants
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Entity
|
||||
@SoftDelete
|
||||
@DynamicUpdate
|
||||
@DynamicInsert
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@Table
|
||||
public class Skill extends SimpleEntity {
|
||||
@Column(nullable = false, length = 100, unique = true, comment = "技能名称")
|
||||
private String name;
|
||||
|
||||
@Column(length = 500, comment = "技能描述")
|
||||
private String description;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.entity.vo;
|
||||
|
||||
public record EmployeeWithCompanyName(
|
||||
String name,
|
||||
String companyName,
|
||||
Integer age,
|
||||
String role
|
||||
) {
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.repository;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Company;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface CompanyRepository extends SimpleRepository<Company> {
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.repository;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Company;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Skill;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Repository
|
||||
public interface EmployeeRepository extends SimpleRepository<Employee> {
|
||||
@EntityGraph(attributePaths = {"company"})
|
||||
@Override
|
||||
Optional<Employee> findOne(Specification<Employee> specification);
|
||||
|
||||
// ==================== 1. 基本字段查询 ====================
|
||||
|
||||
// 单字段精确匹配
|
||||
List<Employee> findByName(String name);
|
||||
|
||||
Optional<Employee> findByCode(String code);
|
||||
|
||||
List<Employee> findByRole(Employee.Role role);
|
||||
|
||||
// 布尔值查询
|
||||
List<Employee> findByActiveTrue();
|
||||
|
||||
List<Employee> findByActiveFalse();
|
||||
|
||||
long countByActiveTrue();
|
||||
|
||||
long countByActiveFalse();
|
||||
|
||||
// ==================== 2. 比较运算符查询 ====================
|
||||
|
||||
List<Employee> findByAgeGreaterThan(Integer age);
|
||||
|
||||
List<Employee> findByAgeLessThan(Integer age);
|
||||
|
||||
List<Employee> findByAgeGreaterThanEqual(Integer age);
|
||||
|
||||
List<Employee> findByAgeLessThanEqual(Integer age);
|
||||
|
||||
List<Employee> findByAgeBetween(Integer startAge, Integer endAge);
|
||||
|
||||
List<Employee> findBySalaryGreaterThan(BigDecimal salary);
|
||||
|
||||
List<Employee> findBySalaryLessThan(BigDecimal salary);
|
||||
|
||||
List<Employee> findBySalaryGreaterThanEqual(BigDecimal salary);
|
||||
|
||||
List<Employee> findBySalaryLessThanEqual(BigDecimal salary);
|
||||
|
||||
List<Employee> findBySalaryBetween(BigDecimal minSalary, BigDecimal maxSalary);
|
||||
|
||||
// ==================== 3. 字符串匹配查询 ====================
|
||||
|
||||
// 精确匹配
|
||||
List<Employee> findByNameContaining(String name);
|
||||
|
||||
List<Employee> findByNameStartingWith(String prefix);
|
||||
|
||||
List<Employee> findByNameEndingWith(String suffix);
|
||||
|
||||
List<Employee> findByNameLike(String pattern);
|
||||
|
||||
// 忽略大小写
|
||||
List<Employee> findByNameContainingIgnoreCase(String name);
|
||||
|
||||
List<Employee> findByNameStartingWithIgnoreCase(String prefix);
|
||||
|
||||
List<Employee> findByNameEndingWithIgnoreCase(String suffix);
|
||||
|
||||
List<Employee> findByNameIgnoreCase(String name);
|
||||
|
||||
// ==================== 4. NULL值和空值查询 ====================
|
||||
|
||||
List<Employee> findByBonusIsNull();
|
||||
|
||||
List<Employee> findByBonusIsNotNull();
|
||||
|
||||
List<Employee> findByResumeIsNull();
|
||||
|
||||
List<Employee> findByResumeIsNotNull();
|
||||
|
||||
List<Employee> findByHobbiesEmpty();
|
||||
|
||||
List<Employee> findByHobbiesIsNotEmpty();
|
||||
|
||||
List<Employee> findBySkillsEmpty();
|
||||
|
||||
List<Employee> findBySkillsIsNotEmpty();
|
||||
|
||||
// ==================== 5. 集合成员查询 (IN/NOT IN) ====================
|
||||
|
||||
List<Employee> findByRoleIn(Set<Employee.Role> roles);
|
||||
|
||||
List<Employee> findByRoleNotIn(Set<Employee.Role> roles);
|
||||
|
||||
List<Employee> findByNameIn(List<String> names);
|
||||
|
||||
List<Employee> findByNameNotIn(List<String> names);
|
||||
|
||||
List<Employee> findByAgeIn(List<Integer> ages);
|
||||
|
||||
List<Employee> findByAgeNotIn(List<Integer> ages);
|
||||
|
||||
// ==================== 6. 逻辑运算查询 (AND/OR/NOT) ====================
|
||||
|
||||
List<Employee> findByNameAndRole(String name, Employee.Role role);
|
||||
|
||||
List<Employee> findByAgeAndActive(Integer age, Boolean active);
|
||||
|
||||
List<Employee> findByNameAndSalaryGreaterThan(String name, BigDecimal salary);
|
||||
|
||||
List<Employee> findByRoleAndActiveAndAgeGreaterThan(Employee.Role role, Boolean active, Integer age);
|
||||
|
||||
List<Employee> findByNameOrRole(String name, Employee.Role role);
|
||||
|
||||
List<Employee> findByAgeLessThanOrSalaryGreaterThan(Integer age, BigDecimal salary);
|
||||
|
||||
List<Employee> findByNameOrCode(String name, String code);
|
||||
|
||||
List<Employee> findByRoleNot(Employee.Role role);
|
||||
|
||||
List<Employee> findByNameNot(String name);
|
||||
|
||||
// ==================== 7. 排序查询 ====================
|
||||
|
||||
List<Employee> findByActiveTrueOrderByAgeAsc();
|
||||
|
||||
List<Employee> findByActiveTrueOrderByAgeDesc();
|
||||
|
||||
List<Employee> findByActiveTrueOrderBySalaryDesc();
|
||||
|
||||
List<Employee> findByRoleOrderByAgeDescNameAsc(Employee.Role role);
|
||||
|
||||
// ==================== 8. 分页查询 ====================
|
||||
|
||||
Page<Employee> findByActiveTrue(Pageable pageable);
|
||||
|
||||
Page<Employee> findByRole(Employee.Role role, Pageable pageable);
|
||||
|
||||
Page<Employee> findBySalaryGreaterThan(BigDecimal salary, Pageable pageable);
|
||||
|
||||
// ==================== 9. 关联查询 (JOIN) ====================
|
||||
|
||||
List<Employee> findByCompany(Company company);
|
||||
|
||||
List<Employee> findByCompanyName(String companyName);
|
||||
|
||||
List<Employee> findByCompanyNameContaining(String companyName);
|
||||
|
||||
List<Employee> findByCompanyMembersGreaterThan(Integer members);
|
||||
|
||||
List<Employee> findBySkillsContaining(Skill skill);
|
||||
|
||||
List<Employee> findBySkillsName(String skillName);
|
||||
|
||||
List<Employee> findBySkillsNameContaining(String skillName);
|
||||
|
||||
List<Employee> findBySkillsNameIn(List<String> skillNames);
|
||||
|
||||
// ==================== 10. 嵌入式对象查询 ====================
|
||||
|
||||
List<Employee> findByAddressCity(String city);
|
||||
|
||||
List<Employee> findByAddressCityContaining(String city);
|
||||
|
||||
List<Employee> findByAddressState(String state);
|
||||
|
||||
List<Employee> findByAddressCountry(String country);
|
||||
|
||||
List<Employee> findByAddressCityAndAddressState(String city, String state);
|
||||
|
||||
// ==================== 11. 复合复杂查询 ====================
|
||||
|
||||
List<Employee> findByNameAndRoleAndAgeGreaterThan(String name, Employee.Role role, Integer age);
|
||||
|
||||
List<Employee> findByRoleAndActiveTrueAndSalaryGreaterThan(Employee.Role role, BigDecimal salary);
|
||||
|
||||
List<Employee> findByNameContainingIgnoreCaseAndActiveTrueAndAgeBetween(String name, Integer minAge, Integer maxAge);
|
||||
|
||||
List<Employee> findByCompanyNameContainingAndActiveTrue(String companyName);
|
||||
|
||||
List<Employee> findBySkillsNameContainingAndAgeGreaterThan(String skillName, Integer age);
|
||||
|
||||
// ==================== 12. DISTINCT 查询 ====================
|
||||
|
||||
List<Employee> findDistinctByRole(Employee.Role role);
|
||||
|
||||
// ==================== 13. TOP/LIMIT 查询 ====================
|
||||
|
||||
List<Employee> findTop5BySalaryGreaterThan(BigDecimal salary);
|
||||
|
||||
Optional<Employee> findFirstByRoleOrderBySalaryDesc(Employee.Role role);
|
||||
|
||||
// ==================== 14. LIKE 模式查询 ====================
|
||||
|
||||
List<Employee> findByNameLikeIgnoreCase(String pattern);
|
||||
|
||||
List<Employee> findByNameStartingWithAndRole(String prefix, Employee.Role role);
|
||||
|
||||
List<Employee> findByNameEndingWithAndAgeLessThan(String suffix, Integer age);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.repository;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Report;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ReportRepository extends SimpleRepository<Report> {
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.service;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Company;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.repository.CompanyRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class CompanyService extends SimpleServiceSupport<Company> {
|
||||
public CompanyService(CompanyRepository repository) {
|
||||
super(repository);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.service;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.repository.EmployeeRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class EmployeeService extends SimpleServiceSupport<Employee> {
|
||||
public EmployeeService(EmployeeRepository repository) {
|
||||
super(repository);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.service;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Report;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.repository.ReportRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ReportService extends SimpleServiceSupport<Report> {
|
||||
public ReportService(ReportRepository repository) {
|
||||
super(repository);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
spring:
|
||||
profiles:
|
||||
include: test
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
|
||||
username: test
|
||||
password: test
|
||||
driver-class-name: org.h2.Driver
|
||||
jpa:
|
||||
generate-ddl: true
|
||||
fenix:
|
||||
print-banner: false
|
||||
@@ -1,66 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.lanyuanxiaoyao</groupId>
|
||||
<artifactId>spring-boot-service-template</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-boot-service-template-database-xbatis</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.lanyuanxiaoyao</groupId>
|
||||
<artifactId>spring-boot-service-template-database-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.xbatis</groupId>
|
||||
<artifactId>xbatis-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.lanyuanxiaoyao</groupId>
|
||||
<artifactId>spring-boot-service-template-database-common-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.configuration;
|
||||
|
||||
import cn.xbatis.core.incrementer.GeneratorFactory;
|
||||
import cn.xbatis.core.mybatis.mapper.BasicMapper;
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.entity.SnowflakeIdGenerator;
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.mapper.MybatisBasicMapper;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@MapperScan(basePackageClasses = MybatisBasicMapper.class, markerInterface = BasicMapper.class)
|
||||
public class MybatisConfiguration {
|
||||
static {
|
||||
GeneratorFactory.register("snowflake", new SnowflakeIdGenerator());
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.common.helper.ObjectHelper;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.controller.SimpleController;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.GlobalResponse;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.Query;
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.entity.SimpleEntity;
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.service.SimpleServiceSupport;
|
||||
import java.util.function.Function;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
/**
|
||||
* 简单控制器支持类,提供基础的CRUD操作实现
|
||||
* <p>
|
||||
* 该类实现了基本的增删改查功能,通过泛型支持不同类型的数据转换。
|
||||
* 子类需要实现对应的Mapper函数来完成实体类与传输对象之间的转换。
|
||||
* </p>
|
||||
*
|
||||
* <h3>设计特点</h3>
|
||||
* <ul>
|
||||
* <li>泛型设计,支持任意实体类型和数据转换</li>
|
||||
* <li>统一的异常处理和事务管理</li>
|
||||
* <li>支持条件查询、分页查询和详情查询</li>
|
||||
* <li>提供抽象的Mapper方法,便于子类实现数据转换逻辑</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>使用说明</h3>
|
||||
* <p>子类需要实现以下抽象方法:</p>
|
||||
* <ul>
|
||||
* <li>saveItemMapper(): 保存项到实体的转换函数</li>
|
||||
* <li>listItemMapper(): 实体到列表项的转换函数</li>
|
||||
* <li>detailItemMapper(): 实体到详情项的转换函数</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <ENTITY> 实体类型,必须继承SimpleEntity
|
||||
* @param <SAVE_ITEM> 保存项类型
|
||||
* @param <LIST_ITEM> 列表项类型
|
||||
* @param <DETAIL_ITEM> 详情项类型
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class SimpleControllerSupport<ENTITY extends SimpleEntity, SAVE_ITEM, LIST_ITEM, DETAIL_ITEM> implements SimpleController<SAVE_ITEM, LIST_ITEM, DETAIL_ITEM> {
|
||||
protected final SimpleServiceSupport<ENTITY> service;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param service 简单服务支持类实例
|
||||
*/
|
||||
public SimpleControllerSupport(SimpleServiceSupport<ENTITY> service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存实体对象
|
||||
* <p>
|
||||
* 将保存项转换为实体对象后保存,返回保存后的实体ID。
|
||||
* 支持新增和更新操作,通过事务保证数据一致性。
|
||||
* </p>
|
||||
*
|
||||
* @param item 需要保存的项
|
||||
* @return 返回保存后的实体ID响应对象,格式:{status: 0, message: "OK", data: 实体ID}
|
||||
* @throws Exception 保存过程中可能抛出的异常
|
||||
*/
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
@PostMapping(SAVE)
|
||||
@Override
|
||||
public GlobalResponse<Long> save(@RequestBody SAVE_ITEM item) throws Exception {
|
||||
var mapper = saveItemMapper();
|
||||
return GlobalResponse.responseSuccess(service.save(mapper.apply(item)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有实体列表
|
||||
* <p>
|
||||
* 查询所有记录,不带任何过滤条件,返回分页格式的数据。
|
||||
* 将实体对象转换为列表项对象后返回。
|
||||
* </p>
|
||||
*
|
||||
* @return 返回实体列表响应对象,格式:{status: 0, message: "OK", data: {items: [...], total: total}}
|
||||
* @throws Exception 查询过程中可能抛出的异常
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@GetMapping(LIST)
|
||||
@Override
|
||||
public GlobalResponse<GlobalResponse.ListItem<LIST_ITEM>> list() throws Exception {
|
||||
var mapper = listItemMapper();
|
||||
var result = service.list();
|
||||
return GlobalResponse.responseListData(
|
||||
result
|
||||
.stream()
|
||||
.map(entity -> {
|
||||
try {
|
||||
return mapper.apply(entity);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.toList(),
|
||||
result.size()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据查询条件获取实体列表
|
||||
* <p>
|
||||
* 支持复杂的查询条件、排序和分页,返回符合条件的数据。
|
||||
* 将实体对象转换为列表项对象后返回。
|
||||
* </p>
|
||||
*
|
||||
* @param query 查询条件对象,包含过滤条件、排序规则和分页信息
|
||||
* @return 返回符合条件的实体列表响应对象,格式:{status: 0, message: "OK", data: {items: [...], total: total}}
|
||||
* @throws Exception 查询过程中可能抛出的异常
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@PostMapping(LIST)
|
||||
@Override
|
||||
public GlobalResponse<GlobalResponse.ListItem<LIST_ITEM>> list(@RequestBody Query query) throws Exception {
|
||||
if (ObjectHelper.isNull(query)) {
|
||||
return GlobalResponse.responseListData();
|
||||
}
|
||||
var mapper = listItemMapper();
|
||||
var result = service.list(query);
|
||||
return GlobalResponse.responseListData(
|
||||
result.items()
|
||||
.stream()
|
||||
.map(entity -> {
|
||||
try {
|
||||
return mapper.apply(entity);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.toList(),
|
||||
result.total()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取实体详情
|
||||
* <p>
|
||||
* 根据主键ID查询单条记录的详细信息,转换为详情项对象后返回。
|
||||
* 如果记录不存在则抛出异常。
|
||||
* </p>
|
||||
*
|
||||
* @param id 实体主键ID
|
||||
* @return 返回实体详情响应对象,格式:{status: 0, message: "OK", data: 详情数据}
|
||||
* @throws Exception 查询过程中可能抛出的异常
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@GetMapping(DETAIL)
|
||||
@Override
|
||||
public GlobalResponse<DETAIL_ITEM> detail(@PathVariable("id") Long id) throws Exception {
|
||||
var mapper = detailItemMapper();
|
||||
return GlobalResponse.responseSuccess(mapper.apply(service.detailOrThrow(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID删除实体对象
|
||||
* <p>
|
||||
* 根据主键ID删除指定的记录,执行成功后返回成功响应。
|
||||
* 通过事务保证删除操作的一致性。
|
||||
* </p>
|
||||
*
|
||||
* @param id 需要删除的实体主键ID
|
||||
* @return 返回删除结果响应对象,格式:{status: 0, message: "OK", data: null}
|
||||
* @throws Exception 删除过程中可能抛出的异常
|
||||
*/
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
@GetMapping(REMOVE)
|
||||
@Override
|
||||
public GlobalResponse<Object> remove(@PathVariable("id") Long id) throws Exception {
|
||||
service.remove(id);
|
||||
return GlobalResponse.responseSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存项映射器,将保存项转换为实体对象
|
||||
* <p>
|
||||
* 子类需要实现此方法,定义保存项到实体的转换逻辑。
|
||||
* </p>
|
||||
*
|
||||
* @return Function<SAVE_ITEM, ENTITY> 保存项到实体的转换函数
|
||||
*/
|
||||
protected abstract Function<SAVE_ITEM, ENTITY> saveItemMapper();
|
||||
|
||||
/**
|
||||
* 列表项映射器,将实体对象转换为列表项
|
||||
* <p>
|
||||
* 子类需要实现此方法,定义实体到列表项的转换逻辑。
|
||||
* </p>
|
||||
*
|
||||
* @return Function<ENTITY, LIST_ITEM> 实体到列表项的转换函数
|
||||
*/
|
||||
protected abstract Function<ENTITY, LIST_ITEM> listItemMapper();
|
||||
|
||||
/**
|
||||
* 详情项映射器,将实体对象转换为详情项
|
||||
* <p>
|
||||
* 子类需要实现此方法,定义实体到详情项的转换逻辑。
|
||||
* </p>
|
||||
*
|
||||
* @return Function<ENTITY, DETAIL_ITEM> 实体到详情项的转换函数
|
||||
*/
|
||||
protected abstract Function<ENTITY, DETAIL_ITEM> detailItemMapper();
|
||||
|
||||
public interface Mapper<S, T> {
|
||||
T map(S source) throws Exception;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.entity;
|
||||
|
||||
import cn.xbatis.db.IdAutoType;
|
||||
import cn.xbatis.db.annotations.TableId;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@FieldNameConstants
|
||||
public class IdOnlyEntity {
|
||||
@TableId(value = IdAutoType.GENERATOR, generator = "snowflake")
|
||||
private Long id;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.entity;
|
||||
|
||||
import cn.xbatis.db.annotations.LogicDelete;
|
||||
import cn.xbatis.db.annotations.LogicDeleteTime;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@FieldNameConstants
|
||||
public class LogicDeleteEntity extends IdOnlyEntity {
|
||||
@LogicDelete
|
||||
private Boolean deleted = false;
|
||||
@LogicDeleteTime
|
||||
private LocalDateTime deletedTime;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.entity;
|
||||
|
||||
import cn.xbatis.db.annotations.TableField;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString(callSuper = true)
|
||||
@FieldNameConstants
|
||||
public class SimpleEntity extends LogicDeleteEntity {
|
||||
@TableField(defaultValue = "{NOW}", defaultValueFillAlways = true)
|
||||
private LocalDateTime createdTime;
|
||||
@TableField(defaultValue = "{NOW}", defaultValueFillAlways = true, updateDefaultValue = "{NOW}", updateDefaultValueFillAlways = true)
|
||||
private LocalDateTime modifiedTime;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.entity;
|
||||
|
||||
import cn.xbatis.core.incrementer.Generator;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.helper.SnowflakeHelper;
|
||||
|
||||
public class SnowflakeIdGenerator implements Generator<Long> {
|
||||
@Override
|
||||
public Long nextId(Class<?> entity) {
|
||||
return SnowflakeHelper.next();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.mapper;
|
||||
|
||||
import cn.xbatis.core.mybatis.mapper.BasicMapper;
|
||||
|
||||
public interface MybatisBasicMapper extends BasicMapper {
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.service;
|
||||
|
||||
import cn.xbatis.core.mybatis.mapper.context.Pager;
|
||||
import cn.xbatis.core.sql.MybatisCmdFactory;
|
||||
import cn.xbatis.core.sql.executor.chain.QueryChain;
|
||||
import com.lanyuanxiaoyao.service.template.common.helper.ObjectHelper;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.Page;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.Query;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.exception.IdNotFoundException;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.service.QueryParser;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.service.SimpleService;
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.entity.SimpleEntity;
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.mapper.MybatisBasicMapper;
|
||||
import db.sql.api.cmd.LikeMode;
|
||||
import db.sql.api.impl.cmd.basic.OrderByDirection;
|
||||
import db.sql.api.impl.cmd.struct.Where;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.mapstruct.Named;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
public abstract class SimpleServiceSupport<ENTITY extends SimpleEntity> implements SimpleService<ENTITY> {
|
||||
private static final int DEFAULT_PAGE_INDEX = 1;
|
||||
private static final int DEFAULT_PAGE_SIZE = 10;
|
||||
|
||||
protected final MybatisBasicMapper mapper;
|
||||
private final Class<ENTITY> target;
|
||||
|
||||
public SimpleServiceSupport(Class<ENTITY> target, MybatisBasicMapper mapper) {
|
||||
this.target = target;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
@Override
|
||||
public Long save(ENTITY entity) {
|
||||
mapper.saveOrUpdate(entity);
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
@Override
|
||||
public void save(Iterable<ENTITY> entities) {
|
||||
mapper.saveOrUpdate(entities);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
@Override
|
||||
public Long count() {
|
||||
return (long) mapper.countAll(target);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
@Override
|
||||
public List<ENTITY> list() {
|
||||
return mapper.listAll(target);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
@Override
|
||||
public List<ENTITY> list(Set<Long> ids) {
|
||||
return mapper.listByIds(target, ids);
|
||||
}
|
||||
|
||||
protected void commonPredicates(Where where) {
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
@Override
|
||||
public Page<ENTITY> list(Query query) {
|
||||
var chain = QueryChain.of(mapper, target);
|
||||
var factory = chain.$();
|
||||
|
||||
var paging = Pager.<ENTITY>of(DEFAULT_PAGE_INDEX, DEFAULT_PAGE_SIZE);
|
||||
if (ObjectHelper.isNotNull(query.page())) {
|
||||
var index = Math.max(ObjectHelper.defaultIfNull(query.page().index(), DEFAULT_PAGE_INDEX), 1);
|
||||
var size = Math.max(ObjectHelper.defaultIfNull(query.page().size(), DEFAULT_PAGE_SIZE), 1);
|
||||
paging = Pager.of(index, size);
|
||||
}
|
||||
|
||||
if (ObjectHelper.isNotEmpty(query.sort())) {
|
||||
query.sort().forEach(sort -> chain.orderBy(OrderByDirection.valueOf(sort.direction().name()), sort.column()));
|
||||
}
|
||||
|
||||
var where = chain.where();
|
||||
commonPredicates(where);
|
||||
new XBatisQueryParser<>(query.query(), where, target, factory).build();
|
||||
|
||||
var pager = chain.paging(paging);
|
||||
|
||||
return new Page<>(pager.getResults(), pager.getTotal());
|
||||
}
|
||||
|
||||
private Optional<ENTITY> detailOptional(Long id) {
|
||||
if (ObjectHelper.isNull(id)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return mapper.getOptionalById(target, id);
|
||||
}
|
||||
|
||||
@Named("detail")
|
||||
@Transactional(readOnly = true)
|
||||
@Override
|
||||
public ENTITY detail(Long id) {
|
||||
return detailOptional(id).orElse(null);
|
||||
}
|
||||
|
||||
@Named("detailOrThrow")
|
||||
@Transactional(readOnly = true)
|
||||
@Override
|
||||
public ENTITY detailOrThrow(Long id) {
|
||||
return detailOptional(id).orElseThrow(() -> new IdNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
@Override
|
||||
public void remove(Long id) {
|
||||
mapper.deleteById(target, id);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
@Override
|
||||
public void remove(Set<Long> ids) {
|
||||
mapper.deleteByIds(target, ids);
|
||||
}
|
||||
|
||||
private static final class XBatisQueryParser<ENTITY> extends QueryParser<Where> {
|
||||
private final Class<ENTITY> target;
|
||||
private final MybatisCmdFactory factory;
|
||||
|
||||
private XBatisQueryParser(Query.Queryable queryable, Where where, Class<ENTITY> target, MybatisCmdFactory factory) {
|
||||
super(queryable, where);
|
||||
this.target = target;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void nullEqual(Query.Queryable queryable, Where where) {
|
||||
queryable.nullEqual().forEach(column -> where.isNull(factory.field(target, column)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notNullEqual(Query.Queryable queryable, Where where) {
|
||||
queryable.notNullEqual().forEach(column -> where.isNotNull(factory.field(target, column)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void empty(Query.Queryable queryable, Where where) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notEmpty(Query.Queryable queryable, Where where) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void equal(Query.Queryable queryable, Where where) {
|
||||
queryable.equal().forEach((column, value) -> where.eq(factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notEqual(Query.Queryable queryable, Where where) {
|
||||
queryable.notEqual().forEach((column, value) -> where.ne(factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void like(Query.Queryable queryable, Where where) {
|
||||
queryable.like().forEach((column, value) -> where.like(LikeMode.NONE, factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notLike(Query.Queryable queryable, Where where) {
|
||||
queryable.notLike().forEach((column, value) -> where.notLike(LikeMode.NONE, factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void contain(Query.Queryable queryable, Where where) {
|
||||
queryable.contain().forEach((column, value) -> where.like(factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notContain(Query.Queryable queryable, Where where) {
|
||||
queryable.notContain().forEach((column, value) -> where.notLike(factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startWith(Query.Queryable queryable, Where where) {
|
||||
queryable.startWith().forEach((column, value) -> where.like(LikeMode.RIGHT, factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notStartWith(Query.Queryable queryable, Where where) {
|
||||
queryable.notStartWith().forEach((column, value) -> where.notLike(LikeMode.RIGHT, factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void endWith(Query.Queryable queryable, Where where) {
|
||||
queryable.endWith().forEach((column, value) -> where.like(LikeMode.LEFT, factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notEndWith(Query.Queryable queryable, Where where) {
|
||||
queryable.notEndWith().forEach((column, value) -> where.notLike(LikeMode.LEFT, factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void great(Query.Queryable queryable, Where where) {
|
||||
queryable.great().forEach((column, value) -> where.gt(factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void less(Query.Queryable queryable, Where where) {
|
||||
queryable.less().forEach((column, value) -> where.lt(factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void greatEqual(Query.Queryable queryable, Where where) {
|
||||
queryable.greatEqual().forEach((column, value) -> where.gte(factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void lessEqual(Query.Queryable queryable, Where where) {
|
||||
queryable.lessEqual().forEach((column, value) -> where.lte(factory.field(target, column), value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void inside(Query.Queryable queryable, Where where) {
|
||||
queryable.inside()
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> ObjectHelper.isNotEmpty(entry.getValue()))
|
||||
.forEach(entry -> where.in(factory.field(target, entry.getKey()), entry.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notInside(Query.Queryable queryable, Where where) {
|
||||
queryable.notInside()
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> ObjectHelper.isNotEmpty(entry.getValue()))
|
||||
.forEach(entry -> where.notIn(factory.field(target, entry.getKey()), entry.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void between(Query.Queryable queryable, Where where) {
|
||||
queryable.between().forEach((column, value) -> where.between(factory.field(target, column), value.start(), value.end()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notBetween(Query.Queryable queryable, Where where) {
|
||||
queryable.notBetween().forEach((column, value) -> where.notBetween(factory.field(target, column), value.start(), value.end()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
create table if not exists Company
|
||||
(
|
||||
id bigint primary key,
|
||||
name varchar(255) not null,
|
||||
members int not null,
|
||||
created_time timestamp not null,
|
||||
modified_time timestamp not null,
|
||||
deleted tinyint not null default 0,
|
||||
deleted_time timestamp
|
||||
);
|
||||
|
||||
create table if not exists Employee
|
||||
(
|
||||
id bigint primary key,
|
||||
name varchar(255) not null,
|
||||
age int not null,
|
||||
company_id bigint not null,
|
||||
created_time timestamp not null,
|
||||
modified_time timestamp not null,
|
||||
deleted tinyint not null default 0,
|
||||
deleted_time timestamp
|
||||
);
|
||||
@@ -1,82 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis;
|
||||
|
||||
import cn.xbatis.core.sql.executor.chain.QueryChain;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.test.AbstractTestApplication;
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.entity.Company;
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.entity.Employee;
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.entity.vo.EmployeeWithCompanyName;
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.mapper.MybatisBasicMapper;
|
||||
import db.sql.api.cmd.LikeMode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@MapperScan("com.lanyuanxiaoyao.service.template.database.xbatis.mapper")
|
||||
@SpringBootApplication
|
||||
public class TestApplication extends AbstractTestApplication {
|
||||
private final MybatisBasicMapper mapper;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TestApplication.class, args);
|
||||
}
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void runTests() {
|
||||
testCrud();
|
||||
testDelete();
|
||||
testQuery();
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private void testDelete() {
|
||||
formatLog("Delete");
|
||||
saveItem("company", randomCompany());
|
||||
saveItem("company", randomCompany());
|
||||
mapper.deleteAll(Company.class);
|
||||
}
|
||||
|
||||
private void testQuery() {
|
||||
formatLog("Added");
|
||||
var company1 = Company.builder().name(randomString(5)).members(randomInt(100)).build();
|
||||
mapper.saveOrUpdate(company1);
|
||||
var company2 = Company.builder().name(randomString(5)).members(randomInt(100)).build();
|
||||
mapper.saveOrUpdate(company2);
|
||||
var employee1 = Employee.builder().name("Tom").age(randomInt(100)).companyId(company1.getId()).build();
|
||||
mapper.saveOrUpdate(employee1);
|
||||
var employee2 = Employee.builder().name(randomString(10)).age(randomInt(100)).companyId(company2.getId()).build();
|
||||
mapper.saveOrUpdate(employee2);
|
||||
|
||||
formatLog("Query");
|
||||
var employees1 = QueryChain.of(mapper, Employee.class)
|
||||
.isNotNull(Employee::getName)
|
||||
.eq(Employee::getName, "Tom")
|
||||
.like(Employee::getName, "To")
|
||||
.like(LikeMode.RIGHT, Employee::getName, "To")
|
||||
.like(LikeMode.LEFT, Employee::getName, "om")
|
||||
.lt(Employee::getAge, 200)
|
||||
.gt(Employee::getAge, 0)
|
||||
.in(Employee::getName, "Tom", "Mike")
|
||||
.between(Employee::getAge, 0, 200)
|
||||
.list();
|
||||
Assert.isTrue(employees1.size() == 1, "查询数量错误");
|
||||
|
||||
formatLog("Query Join");
|
||||
var employees2 = QueryChain.of(mapper, Employee.class)
|
||||
.select(Employee::getName, Employee::getAge)
|
||||
.select(Company::getId, c -> c.as(EmployeeWithCompanyName::getCompanyName))
|
||||
.leftJoin(Employee.class, Company.class, on -> on.eq(Employee::getCompanyId, Company::getId).gt(Company::getMembers, 0))
|
||||
.eq(Employee::getName, "Tom")
|
||||
.lt(Company::getMembers, 200)
|
||||
.returnType(EmployeeWithCompanyName.class)
|
||||
.list();
|
||||
Assert.isTrue(employees2.size() == 1, "查询数量错误");
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.entity.Company;
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.service.CompanyService;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.function.Function;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("company")
|
||||
public class CompanyController extends SimpleControllerSupport<Company, CompanyController.SaveItem, CompanyController.ListItem, CompanyController.DetailItem> {
|
||||
public CompanyController(CompanyService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<SaveItem, Company> saveItemMapper() {
|
||||
return item -> {
|
||||
var company = new Company();
|
||||
company.setId(item.id());
|
||||
company.setName(item.name());
|
||||
company.setMembers(item.members());
|
||||
return company;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Company, ListItem> listItemMapper() {
|
||||
return company -> new ListItem(
|
||||
company.getId(),
|
||||
company.getName(),
|
||||
company.getMembers()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<Company, DetailItem> detailItemMapper() {
|
||||
return company -> new DetailItem(
|
||||
company.getId(),
|
||||
company.getName(),
|
||||
company.getMembers(),
|
||||
company.getCreatedTime(),
|
||||
company.getModifiedTime()
|
||||
);
|
||||
}
|
||||
|
||||
public record SaveItem(
|
||||
Long id,
|
||||
String name,
|
||||
Integer members
|
||||
) {
|
||||
}
|
||||
|
||||
public record ListItem(
|
||||
Long id,
|
||||
String name,
|
||||
Integer members
|
||||
) {
|
||||
}
|
||||
|
||||
public record DetailItem(
|
||||
Long id,
|
||||
String name,
|
||||
Integer members,
|
||||
LocalDateTime createdTime,
|
||||
LocalDateTime modifiedTime
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.entity;
|
||||
|
||||
import cn.xbatis.db.annotations.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString(callSuper = true)
|
||||
@FieldNameConstants
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Table
|
||||
public class Company extends SimpleEntity {
|
||||
private String name;
|
||||
private Integer members;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.entity;
|
||||
|
||||
import cn.xbatis.db.annotations.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString(callSuper = true)
|
||||
@FieldNameConstants
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Table
|
||||
public class Employee extends SimpleEntity {
|
||||
private String name;
|
||||
private Integer age;
|
||||
|
||||
private Long companyId;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.entity.vo;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString(callSuper = true)
|
||||
@FieldNameConstants
|
||||
public class EmployeeWithCompanyName {
|
||||
private String name;
|
||||
private Integer age;
|
||||
private String companyName;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.service;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.entity.Company;
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.mapper.MybatisBasicMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class CompanyService extends SimpleServiceSupport<Company> {
|
||||
public CompanyService(MybatisBasicMapper mapper) {
|
||||
super(Company.class, mapper);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.xbatis.service;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.entity.Employee;
|
||||
import com.lanyuanxiaoyao.service.template.database.xbatis.mapper.MybatisBasicMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class EmployeeService extends SimpleServiceSupport<Employee> {
|
||||
public EmployeeService(MybatisBasicMapper mapper) {
|
||||
super(Employee.class, mapper);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
spring:
|
||||
profiles:
|
||||
include: test
|
||||
datasource:
|
||||
url: "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=MySQL;DATABASE_TO_LOWER=TRUE;INIT=runscript from '/Users/lanyuanxiaoyao/Project/IdeaProjects/spring-boot-service-template/spring-boot-service-template-database/spring-boot-service-template-database-xbatis/src/test/initial.sql'"
|
||||
username: test
|
||||
password: test
|
||||
driver-class-name: org.h2.Driver
|
||||
mybatis:
|
||||
configuration:
|
||||
banner: false
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.configuration;
|
||||
package com.lanyuanxiaoyao.service.template.database.configuration;
|
||||
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||
import jakarta.persistence.EntityManager;
|
||||
@@ -11,4 +11,4 @@ public class QueryDSLConfiguration {
|
||||
public JPAQueryFactory jpaQueryFactory(EntityManager manager) {
|
||||
return new JPAQueryFactory(manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.controller;
|
||||
package com.lanyuanxiaoyao.service.template.database.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.GlobalResponse;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.Query;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.GlobalResponse;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.Query;
|
||||
|
||||
/**
|
||||
* 查询控制器接口,用于定义统一的查询实体详情和列表的接口规范
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.controller;
|
||||
package com.lanyuanxiaoyao.service.template.database.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.GlobalResponse;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.GlobalResponse;
|
||||
|
||||
/**
|
||||
* 删除控制器接口,用于定义统一的删除实体对象的接口规范
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.controller;
|
||||
package com.lanyuanxiaoyao.service.template.database.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.GlobalResponse;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.GlobalResponse;
|
||||
|
||||
/**
|
||||
* 保存控制器接口,用于定义统一的保存实体对象的接口规范
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.controller;
|
||||
package com.lanyuanxiaoyao.service.template.database.controller;
|
||||
|
||||
public interface SimpleController<SAVE_ITEM, LIST_ITEM, DETAIL_ITEM> extends SaveController<SAVE_ITEM>, QueryController<LIST_ITEM, DETAIL_ITEM>, RemoveController {
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.controller;
|
||||
package com.lanyuanxiaoyao.service.template.database.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.common.helper.ObjectHelper;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.controller.SimpleController;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.GlobalResponse;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.Query;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.SimpleEntity;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.service.SimpleServiceSupport;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.GlobalResponse;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.Query;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.SimpleEntity;
|
||||
import com.lanyuanxiaoyao.service.template.database.service.SimpleServiceSupport;
|
||||
import java.util.function.Function;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.entity;
|
||||
package com.lanyuanxiaoyao.service.template.database.entity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -356,4 +356,4 @@ public record GlobalResponse<T>(Integer status, String message, T data) {
|
||||
*/
|
||||
public record DetailItem<T>(T item) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||
package com.lanyuanxiaoyao.service.template.database.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.entity;
|
||||
package com.lanyuanxiaoyao.service.template.database.entity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -28,4 +28,4 @@ import java.util.List;
|
||||
* @param total 总记录数,用于计算总页数和显示分页信息
|
||||
*/
|
||||
public record Page<ENTITY>(List<ENTITY> items, long total) {
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.entity;
|
||||
package com.lanyuanxiaoyao.service.template.database.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||
package com.lanyuanxiaoyao.service.template.database.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||
package com.lanyuanxiaoyao.service.template.database.entity;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -10,4 +10,4 @@ import org.hibernate.annotations.IdGeneratorType;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD })
|
||||
public @interface SnowflakeId {
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||
package com.lanyuanxiaoyao.service.template.database.entity;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.common.helper.SnowflakeHelper;
|
||||
import com.lanyuanxiaoyao.service.template.database.helper.SnowflakeHelper;
|
||||
import java.io.Serializable;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
@@ -17,4 +17,4 @@ public class SnowflakeIdGenerator implements IdentifierGenerator {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.exception;
|
||||
package com.lanyuanxiaoyao.service.template.database.exception;
|
||||
|
||||
public class IdNotFoundException extends RuntimeException {
|
||||
public IdNotFoundException(Long id) {
|
||||
super("ID为 %d 的资源不存在".formatted(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.exception;
|
||||
package com.lanyuanxiaoyao.service.template.database.exception;
|
||||
|
||||
public class NotCollectionException extends RuntimeException {
|
||||
public NotCollectionException(String variable) {
|
||||
super("变量 %s 不是集合".formatted(variable));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.exception;
|
||||
package com.lanyuanxiaoyao.service.template.database.exception;
|
||||
|
||||
public class NotComparableException extends RuntimeException {
|
||||
public NotComparableException(String variable) {
|
||||
super("变量 %s 不能比较".formatted(variable));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.exception;
|
||||
package com.lanyuanxiaoyao.service.template.database.exception;
|
||||
|
||||
public class NotStringException extends RuntimeException {
|
||||
public NotStringException(String variable) {
|
||||
super("变量 %s 不是字符串".formatted(variable));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.helper;
|
||||
package com.lanyuanxiaoyao.service.template.database.helper;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import java.io.IOException;
|
||||
@@ -115,7 +115,7 @@ public class DatabaseHelper {
|
||||
package %s.repository;
|
||||
|
||||
import %s;
|
||||
import com.lanyuanxiaoyao.service.template.repository.SimpleRepository;
|
||||
import com.lanyuanxiaoyao.service.template.database.repository.SimpleRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
@@ -134,7 +134,7 @@ public class DatabaseHelper {
|
||||
|
||||
import %s;
|
||||
import %s.repository.%sRepository;
|
||||
import com.lanyuanxiaoyao.service.template.service.SimpleServiceSupport;
|
||||
import com.lanyuanxiaoyao.service.template.database.service.SimpleServiceSupport;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -158,7 +158,7 @@ public class DatabaseHelper {
|
||||
|
||||
import %s;
|
||||
import %s.service.%sService;
|
||||
import com.lanyuanxiaoyao.service.template.controller.SimpleControllerSupport;
|
||||
import com.lanyuanxiaoyao.service.template.database.controller.SimpleControllerSupport;
|
||||
import java.util.function.Function;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -197,7 +197,7 @@ public class DatabaseHelper {
|
||||
}
|
||||
}
|
||||
|
||||
""".formatted(projectRootPackage, className, projectRootPackage, name, camelConvert(name), name, name, name, name, name, name, name, name, name, name));
|
||||
""".formatted(projectRootPackage, className, projectRootPackage, name, camelConvert(name), name, name, name, name, name, name, name, name, name, name, name));
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Failed to load entity class: " + className, e);
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.helper;
|
||||
package com.lanyuanxiaoyao.service.template.database.helper;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@@ -65,4 +65,4 @@ public class SnowflakeHelper {
|
||||
private static long nowTimestamp() {
|
||||
return Instant.now().toEpochMilli();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.repository;
|
||||
package com.lanyuanxiaoyao.service.template.database.repository;
|
||||
|
||||
import com.blinkfox.fenix.jpa.FenixJpaRepository;
|
||||
import com.blinkfox.fenix.specification.FenixJpaSpecificationExecutor;
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.service;
|
||||
package com.lanyuanxiaoyao.service.template.database.service;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.common.helper.ObjectHelper;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.Query;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.Query;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@@ -125,4 +125,4 @@ public abstract class QueryParser<O> {
|
||||
notBetween(queryable, container);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.service;
|
||||
package com.lanyuanxiaoyao.service.template.database.service;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.Page;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.Query;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.Page;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.Query;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.service;
|
||||
package com.lanyuanxiaoyao.service.template.database.service;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.service;
|
||||
package com.lanyuanxiaoyao.service.template.database.service;
|
||||
|
||||
/**
|
||||
* 保存服务接口,用于定义统一的保存实体对象的服务规范
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.common.service;
|
||||
package com.lanyuanxiaoyao.service.template.database.service;
|
||||
|
||||
public interface SimpleService<ENTITY> extends SaveService<ENTITY>, QueryService<ENTITY>, RemoveService<ENTITY> {
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
package com.lanyuanxiaoyao.service.template.database.jpa.service;
|
||||
package com.lanyuanxiaoyao.service.template.database.service;
|
||||
|
||||
import com.lanyuanxiaoyao.service.template.common.helper.ObjectHelper;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.Page;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.entity.Query;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.exception.IdNotFoundException;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.exception.NotCollectionException;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.exception.NotComparableException;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.exception.NotStringException;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.service.QueryParser;
|
||||
import com.lanyuanxiaoyao.service.template.database.common.service.SimpleService;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.IdOnlyEntity;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.SimpleEntity;
|
||||
import com.lanyuanxiaoyao.service.template.database.jpa.repository.SimpleRepository;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.Page;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.Query;
|
||||
import com.lanyuanxiaoyao.service.template.database.exception.IdNotFoundException;
|
||||
import com.lanyuanxiaoyao.service.template.database.exception.NotCollectionException;
|
||||
import com.lanyuanxiaoyao.service.template.database.exception.NotComparableException;
|
||||
import com.lanyuanxiaoyao.service.template.database.exception.NotStringException;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.IdOnlyEntity;
|
||||
import com.lanyuanxiaoyao.service.template.database.entity.SimpleEntity;
|
||||
import com.lanyuanxiaoyao.service.template.database.repository.SimpleRepository;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Path;
|
||||
Reference in New Issue
Block a user