# sorm **Repository Path**: parselife/sorm ## Basic Information - **Project Name**: sorm - **Description**: ☕ SORM:java开发的一个轻量、简单易用的 `ORM` - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: https://cat-eat-bat.github.io/vjpress/guide/sorm/intro.html - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2025-06-16 - **Last Updated**: 2025-11-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: Java, ORM, SpringBoot, postgres, 数据库ORM ## README # 介绍 **SORM** 是一个轻量、简单易用的关系型数据库 ORM。当你厌烦了 Mybatis(plus) 的 xml 写法以及 JPA 的复杂配置时,你可以试试 `SORM` :yellow_heart: 。 ## 特性 - 支持常见数据库 - 支持多数据源 - 支持数据源配置加密 - 支持运行时数据库表自动生成(DDL) - 支持数据表字段自动生成(DML) - 框架集成支持 `springboot`、`javalin` 等 - 对空间数据操作支持 `postgis` - 内置支持两种数据库连接池(`hikari`/`druid`, 可在配置中切换,默认 `hikari`) ## 数据库支持 - Mysql v5.7+ - H2数据库 - PostgresSQL(postgis) - 达梦数据库(`v8`) - 人大金仓 - duckdb - mongodb(`wip`) ## 框架支持 - 支持 SpringBoot 2.x - 支持 Javalin 4.x ## 使用 本章节将介绍如何使用 `SORM`, 代码可参考工程中的 `sorm-example` 模块 ### 1. 引入依赖 ```xml org.winterfell app-spring-boot-starter 1.4.0-SNAPSHOT org.winterfell sorm-spring-boot-starter 1.7.0-SNAPSHOT com.h2database h2 2.2.220 ``` ### 2. 配置数据库连接信息 在 `application.yml` 中配置数据库连接信息: ```yaml{16-27} server: port: 8000 spring: application: name: sorm-sample profiles: active: local h2: console: enabled: true swagger: title: Sorm 示例应用 description: 示例应用描述 version: 1.0 sorm: data-sources: default: url: jdbc:h2:~/Data/swap/demo;AUTO_SERVER=true;CASE_INSENSITIVE_IDENTIFIERS=TRUE username: sa password: driver-class-name: org.h2.Driver settings: ddl-auto: create_if_not_exist show-sql: true entity-packages: org.winterfell.sample.sorm.domain.entity dev-mode: true ``` ### 3. 创建实体类 ::: code-group ```java{3} [Student.java] @Data @Accessors(chain = true) @Table public class Student extends Model { @Id(autoStrategy = IdAutoStrategy.snow_flake) private String id; private String name; private int age; private String address; private String email; private int deleted; @Column(definition = "text") @TypeHandler(JsonTextTypeHandler.class) private Map attributes; @Column(updatable = false) private LocalDateTime createAt; @Column(insertable = false) private LocalDateTime updateAt; } ``` ```java [JsonTextTypeHandler.java] // 用于在 h2 数据库中转换 map 格式字段 public class JsonTextTypeHandler implements FieldTypeHandler> { Gson gson = new GsonBuilder() .enableComplexMapKeySerialization() .serializeNulls() .setPrettyPrinting() .setVersion(1.0) .create(); /** * 从数据库结果获取实际 entity 字段值 * eg: PGgeometry --> geojson * * @param record * @param columnName * @return */ @Override public Map getResult(Record record, String columnName) { return gson.fromJson(record.getStr(columnName), Map.class); } /** * 将 java 实体值正确地设置到数据库对象中 * * @param record 数据库对象 * @param columnName 数据库列名 * @param parameter 实体参数 */ @Override public void setRecord(Record record, String columnName, Map parameter) { record.set(columnName, Objects.isNull(parameter) ? "{}" : gson.toJson(parameter)); } } ``` ::: ### 4. 编写 dao & service & controller ::: code-group ```java [StudentDao.java] @Dao public interface StudentDao extends BaseDao { /** * 根据名字查询实体 */ @Select("select * from student where name like concat('%', #para(name), '%')") List findByName(@Param("name") String name); } ``` ```java [StudentService.java] @Service public class StudentService { @Resource private StudentDao studentDao; public List queryByName(String name) { return studentDao.findByName(name); } public Student createOne(StudentAddCmd cmd) { Student student = new Student() .setName(cmd.getName()) .setAge(cmd.getAge()) .setAddress(cmd.getAddress()) .setDeleted(0) .setEmail(cmd.getEmail()) .setAttributes(cmd.getAttributes()); boolean saved = studentDao.save(student); return saved ? student : null; } public Page queryPage(StudentQuery query) { return studentDao.queryForPage(query); } } ``` ```java [StudentQuery.java] public class StudentQuery extends AbstractPageableAndSortableQuery { /** * 模糊匹配name */ @CqeField(queryType = QueryType.like) private String name; /** * 范围查询 */ @CqeField(queryType = QueryType.range) private AgeRange age; /** * 默认的排序方式 * * @return */ @Override public String defaultOrderBy() { return "create_at:desc"; } public String getName() { return name; } public StudentQuery setName(String name) { this.name = name; return this; } public AgeRange getAge() { return age; } public StudentQuery setAge(AgeRange age) { this.age = age; return this; } public static class AgeRange implements Range { private Integer lower; private Integer upper; public AgeRange() { } @Override public Integer getLower() { return lower; } @Override public Integer getUpper() { return upper; } } } ``` ```java [ApiController.java] @Tag(name="Sorm Demo Api") @RestController @EnableRespAdvice @RequestMapping("/api/v1/students") public class ApiController { private final StudentService studentService; public ApiController(StudentService studentService) { this.studentService = studentService; } @ApiOperation("新增") @PostMapping("") public Student add(@RequestBody StudentAddCmd cmd) { return studentService.createOne(cmd); } @ApiOperation("分页查询") @PostMapping("/query") public Page pageQuery(@RequestBody StudentQuery query) { return studentService.queryPage(query); } } ``` ::: ### 5. 修改入口类 ```java @SpringBootApplication(scanBasePackages = "org.winterfell") @DaoScan("org.winterfell.sample.sorm.dao") // 这里配置dao扫描的位置 // [!code focus] public class SormSampleApplication { public static void main(String[] args) { SpringApplication.run(SormSampleApplication.class, args); } } ``` ### 6. 启动项目 启动成功后,访问 `http://127.0.0.1:8000/doc.html` 查看接口文档