代码拉取完成,页面将自动刷新
基于JdbcTemplate实现的快速开发框架,简单高效。
引入jar包:
<dependency>
<groupId>io.github.ramerf</groupId>
<artifactId>wind-core</artifactId>
<version>4.0.7</version>
</dependency>
新建实体Foo
@TableInfo // 该注解是可选的
public class Foo {
// 实体必须有主键
@javax.persistence.Id private Long id;
}
OK,可以使用了!
GenericService service = GenericService.with(Foo.class, Long.class);
// service可以操作任意实体 service.create(bar)
final Cnds<Foo> cnds = Cnds.of(Foo.class).gt(Foo::setId, 0L).limit(1, 10).orderBy(Foo::getName);
service.page(cnds);
需要继承于BaseService
public interface FooService extends BaseService<Foo, Long> {}
参考 wind-demo
模块 和 wind-core
测试代码
@Slf4j
@Sql("classpath:db-mysql.sql")
@ExtendWith(SpringExtension.class)
@ActiveProfiles("mysql")
@SpringBootTest(classes = MysqlApplication.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@DisplayName("Mysql 测试")
public class BaseServiceTest {
private GenericService<Foo, Long> service;
private static final Foo foo;
private static final Long id = 10000L;
static {
foo =
Foo.builder()
.id(1L)
.name("test")
.textString("textString")
.bigDecimal(BigDecimal.valueOf(100))
.type(Type.SPORT)
.column("non_match_column")
.bitSet(BitSet.valueOf(new byte[] {0x11, 0x0, 0x1, 0x1, 0x0}))
// .bigText("")
.isNumber(false)
.isNull(false)
.string(true)
.nonNull(true)
.typeJson(Type.SPORT)
.typesJson(Arrays.asList(Type.PHONE, Type.SPORT))
.createTime(LocalDateTime.now())
.build();
}
@BeforeEach
public void before() {
foo.setId(id);
service = GenericService.with(Foo.class, Long.class);
}
@Test
@DisplayName("条件构造工具类")
public void testCnds() {
final Cnds<Foo> cnds =
// 指定操作的表
Cnds.of(Foo.class)
// 条件
.gt(Foo::setId, 0L)
// 自定义sql
.and("name is not null")
// 分页
.limit(1, 10)
// 分组
.groupBy(Foo::getName)
// 排序
.orderBy(Foo::getId, Direction.DESC);
log.info("testCnds:[{}]", cnds.getString());
final QueryColumn<Foo> queryColumn =
QueryColumn.of(Foo.class)
// 查询指定列
.col(Foo::getId)
.col(Foo::getName)
// 自定义sql
.col(
"(case name when 'halo1' then '匹配1' when 'halo2' then '匹配2' else '未匹配' end) as name,id");
log.info("testCnds:[{}]", queryColumn.getString());
}
@Test
@DisplayName("统计")
public void testCount() {
final Cnds<Foo> cnds = Cnds.of(Foo.class).gt(Foo::setId, 0L);
assertTrue(service.count(cnds) > 0);
}
@Test
@DisplayName("查询单个")
public void testGetOne() {
// 通过id查询
assertNotNull(service.getById(id));
// 条件查询
assertNotNull(service.getOne(Cnds.of(Foo.class).eq(Foo::setId, id)));
// 条件查询指定列
final Cnds<Foo> cnds = Cnds.of(Foo.class).eq(Foo::setId, id);
final QueryColumn<Foo> queryColumn =
QueryColumn.of(Foo.class).col(Foo::getName).col(Foo::getId);
assertNotNull(service.getOne(cnds, queryColumn));
// 条件查询排序
assertNotNull(service.getOne(Cnds.of(Foo.class).limit(1).orderBy(Foo::getId, Direction.DESC)));
// 返回任意对象
assertNotNull(
service.getOne(
Cnds.of(Foo.class).eq(Foo::setId, id),
QueryColumn.of(Foo.class).col(Foo::getId),
IdNameResponse.class));
// 自定义sql
service.getOne(
Cnds.of(Foo.class).eq(Foo::setId, id).and("name is not null"),
QueryColumn.of(Foo.class)
.col(
"(case name when 'halo1' then '匹配1' when 'halo2' then '匹配2' else '未匹配' end) as name,id"));
// 自定义sql
assertNotNull(service.fetchOneBySql("select id,name from foo limit 1", IdNameResponse.class));
}
@Test
@DisplayName("查询列表")
public void testList() {
// 通过id列表查询
assertNotNull(service.listByIds(Arrays.asList(id, 2L, 3L)));
final Cnds<Foo> cnds = Cnds.of(Foo.class).eq(Foo::setId, id);
// 条件查询
assertNotNull(service.list(cnds));
// 查询指定列
final QueryColumn<Foo> queryColumn =
QueryColumn.of(Foo.class).col(Foo::getName).col(Foo::getId);
assertNotNull(service.list(cnds, queryColumn, IdNameResponse.class));
// 查询指定页
cnds.limit(1, 10).orderBy(Foo::getId, Direction.DESC);
assertNotNull(service.list(cnds));
// 指定返回对象
assertNotNull(service.list(cnds, IdNameResponse.class));
}
@Test
@DisplayName("查询分页")
public void testPage() {
final Cnds<Foo> cnds = Cnds.of(Foo.class).gt(Foo::setId, 0L).limit(1, 10).orderBy(Foo::getName);
assertNotNull(service.page(cnds));
// 指定列
assertNotNull(service.page(cnds, QueryColumn.of(Foo.class).col(Foo::getId).col(Foo::getName)));
}
@Test
@Order(2)
@DisplayName("单个创建")
@Transactional(rollbackFor = Exception.class)
public void testCreate() {
foo.setId(null);
assertTrue(service.create(foo) > 0);
// 保存指定字段
final Fields<Foo> fields =
Fields.of(Foo.class).include(Foo::getAge, Foo::isString, Foo::isNumber);
// 排除指定字段
// fields.exclude(Foo::getAge)
assertTrue(service.create(foo, fields) > 0);
// 返回当前对象相当于 create + getOne
assertNotNull(service.createAndGet(foo));
}
@Test
@DisplayName("批量创建")
@Transactional(rollbackFor = Exception.class)
public void testCreateBatch() {
foo.setId(null);
final List<Foo> list =
LongStream.range(1, 101)
.mapToObj(
i ->
Foo.builder()
.name("test" + i)
.textString("text" + i)
.bigDecimal(BigDecimal.valueOf(100 + i))
.type(Type.SPORT)
.column("non_match_column")
.build())
.collect(toList());
long start = System.currentTimeMillis();
assertFalse(
service
.createBatch(
list,
Fields.of(Foo.class)
.include(
Foo::getName,
Foo::getTextString,
Foo::getBigText,
Foo::getType,
Foo::getColumn))
.isPresent());
}
@Test
@DisplayName("单个更新")
@Transactional(rollbackFor = Exception.class)
public void testUpdate() {
assertEquals(service.update(foo), 1);
foo.setName("<" + LocalDateTime.now() + ">");
// 指定属性
assertEquals(service.update(foo, Fields.of(Foo.class).include(Foo::getName)), 1);
// 条件更新
assertEquals(service.update(foo, Cnds.of(Foo.class).eq(Foo::setId, id)), 1);
// 条件更新指定字段
assertEquals(
service.update(
foo, //
Fields.of(Foo.class).include(Foo::getName),
Cnds.of(Foo.class).eq(Foo::setId, id)),
1);
// 返回当前对象相当于 update + getOne
assertNotNull(service.updateAndGet(foo));
}
@Test
@DisplayName("批量更新")
@Transactional(rollbackFor = Exception.class)
public void testUpdateBatch() {
final List<Foo> list =
LongStream.range(1, 101)
.mapToObj(
i ->
Foo.builder()
.id(i)
.name("test" + i * i)
.textString("text" + i)
.bigDecimal(BigDecimal.valueOf(100 + i))
.type(Type.SPORT)
.column("non_match_column")
.build())
.collect(toList());
// 可选指定更新字段
assertFalse(service.updateBatch(list, Fields.of(Foo.class).include(Foo::getName)).isPresent());
}
@Test
@Order(20)
@DisplayName("删除")
@Transactional(rollbackFor = Exception.class)
public void testDelete() {
// 通过id删除
assertEquals(service.delete(id), 1);
// 通过id列表删除
assertTrue(service.deleteByIds(Arrays.asList(id, 2L, 3L, 4L)).orElse(0) > 0);
// 条件删除
assertEquals(service.delete(Cnds.of(Foo.class).eq(Foo::setId, id)), 1);
}
@Test
@Order(21)
@DisplayName("域对象Domain")
@Transactional(rollbackFor = Exception.class)
public void testDomain() {
// 需要对象继承Domain: public class Foo extends Domain<Foo, Long>
foo.setId(null);
assertTrue(foo.create() > 0);
foo.setId(id);
assertTrue(foo.update(Fields.of(Foo.class).include(Foo::getName)) > 0);
assertTrue(foo.delete(Cnds.of(Foo.class).eq(Foo::setId, id)) > 0);
}
/**
* @author ramer
* @since 2020/8/5
*/
@Getter
@Setter
public static class IdNameResponse {
private Long id;
private String name;
}
}
@Bean
public ITypeHandler customTypeHandler() {}
wind:
# 自动建表,扫描entity-package下包含@TableInfo的类.可选值:none,create,update.默认:none
ddl-auto: update
@Bean
public InterEnumSerializer interEnumSerializer() {
return InterEnum::value;
}
@Bean
public InterEnumSerializer interEnumSerializer() {
return interEnum -> {
JSONObject jsonObject = new JSONObject();
jsonObject.put("key", interEnum.value());
jsonObject.put("value", interEnum.desc());
return jsonObject;
};
}
默认id自增,以下是雪花算法写法
@Bean
public IdGenerator autoIncrementGenerator() {
return new SnowflakeIdGenerator();
}
wind:
cache:
# 缓存类型.可选值: redis,memory,none 默认none禁用缓存
type: redis
# 缓存key前缀
key-prefix: io.github.ramerf.wind
@Resource private Cache cache;
cache.clear(Foo.class);
wind:
logic-delete-prop:
# 是否启用逻辑删除,可以在类上使用@TableInfo(logicDelete = @LogicDelete(enable = true))属性覆盖
enable: false
# 逻辑删除字段名
field-name: deleted
# 逻辑未删除值(默认为 false)
not-delete: false
# 逻辑已删除值(默认为 true)
deleted: true
# entity所在包路径,多个以,分割;如果不需要自动建表可以不配置该项
entity-package: io.github.ramerf.wind.demo.entity.pojo
# 自动建表,扫描entity-package下包含@TableInfo的类.可选值:none,create,update.默认:none
ddl-auto: update
# 批量操作时每次处理的大小,默认为150
batch-size: 500
# 是否自定义枚举反序列化,默认为false.设置为true时,可能需要编写枚举反序列化代码
custom-enum-deserializer: false
cache:
# 缓存类型.可选值: redis,memory,none 默认none禁用缓存
type: redis
# 缓存key前缀
key-prefix: io.github.ramerf.wind
# 用于雪花算法
snowflake-prop:
worker-id: 3
data-center-id: 2
# 新增/更新时写入值为null的属性,默认写入
write-null-prop: false
代码格式化使用: google-java-format
欢迎提Issue 和 Pull request
如果您在使用本项目时遇到问题,请邮件联系
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。