{"id":21368098,"url":"https://github.com/veasion/veasion-db","last_synced_at":"2025-08-11T17:08:25.339Z","repository":{"id":41171733,"uuid":"435107688","full_name":"veasion/veasion-db","owner":"veasion","description":"一个轻量级持久层ORM框架，该框架提供丰富灵活的数据库操作，框架无需写SQL，支持任意查询或更新。","archived":false,"fork":false,"pushed_at":"2024-01-30T12:15:31.000Z","size":387,"stargazers_count":140,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-07-08T05:03:12.581Z","etag":null,"topics":["jdbc","orm-framework","sql"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/veasion.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-05T08:02:13.000Z","updated_at":"2025-07-07T06:53:02.000Z","dependencies_parsed_at":"2023-12-01T04:27:17.942Z","dependency_job_id":null,"html_url":"https://github.com/veasion/veasion-db","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/veasion/veasion-db","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veasion%2Fveasion-db","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veasion%2Fveasion-db/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veasion%2Fveasion-db/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veasion%2Fveasion-db/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/veasion","download_url":"https://codeload.github.com/veasion/veasion-db/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veasion%2Fveasion-db/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269922906,"owners_count":24496999,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-11T02:00:10.019Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jdbc","orm-framework","sql"],"created_at":"2024-11-22T07:22:45.903Z","updated_at":"2025-08-11T17:08:25.313Z","avatar_url":"https://github.com/veasion.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# veasion-db\n\nveasion-db 是一个轻量级持久层ORM框架，除slf4j-api外不依赖任何第三方jar，该框架提供丰富灵活的数据库操作，\n单元测试 query/update 目录下有大量示例及demo。\n\n框架无需写SQL，基本支持任意查询或更新，如多表关联查询、多表关联更新、子查询、union、with、window、insert select、replace、不同数据库分页等。\n\n框架支持自定义拦截器，内置逻辑删除、数据隔离拦截器，可通过SPI或调用InterceptorUtils.addInterceptor方法加入扩展。\n## maven 依赖\n添加 veasion-db 依赖\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecn.veasion\u003c/groupId\u003e\n    \u003cartifactId\u003eveasion-db\u003c/artifactId\u003e\n    \u003cversion\u003e1.2.7\u003c/version\u003e\n\u003c/dependency\u003e\n```\n支持sql解析生成veasion-db代码\n```\nString sql = \"select * from t_student where id = 1\";\nString code = SQLParseUtils.parseSQLConvert(sql);\n// 直接把SQL转换成对应的代码，示例参考单元测试 SqlDbConvertTest\n\n// 该功能为扩展功能需要加入第三方依赖，示例见单元测试 SqlDbConvertTest\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.jsqlparser\u003c/groupId\u003e\n    \u003cartifactId\u003ejsqlparser\u003c/artifactId\u003e\n    \u003cversion\u003e1.2\u003c/version\u003e\n\u003c/dependency\u003e\n\n```\n## 使用方式介绍\n这里以 student 表举例\n```java\n// 自定义 dao 继承 JdbcEntityDao\u003cT, ID\u003e 类\npublic class StudentDao extends JdbcEntityDao\u003cStudentPO, Long\u003e {\n\n}\n\n// 数据源通过SPI提供，实现 cn.veasion.db.jdbc.DataSourceProvider 接口即可\n\n// 使用\n// StudentDao studentDao = new StudentDao();\n\n// StudentPO student = studentDao.getById(1L);\n```\n### 简单查询示例\n```java\npublic class SimpleQueryTest extends BaseTest {\n\n    public static void main(String[] args) {\n        // 查询全部学生（*）\n        // select * from t_student\n        studentDao.queryList(new Q());\n\n        // 查询全部学生（全部字段）\n        // select id, sno, name, class_id, sex, age, `desc`, version, is_deleted, create_time, update_time from t_student\n        studentDao.queryList(new Q().selectAllWithNoAsterisk());\n\n        // 根据id查询学生\n        // select * from t_student where id = 1\n        studentDao.getById(1L);\n\n        // 根据id查询学生性别（性别转枚举）\n        // select sex from t_student where id = 1\n        SexEnum sexEnum = studentDao.queryForType(new Q(\"sex\").eq(\"id\", 1), SexEnum.class);\n\n        // 查询学号为s001的学生名称\n        // select name from t_student where sno = 's001'\n        studentDao.queryForType(new Q(\"name\").eq(\"sno\", \"s001\"), String.class);\n        // lambda\n        studentDao.queryForType(new LambdaQuery\u003c\u003e(StudentPO::getName).eq(StudentPO::getSno, \"s001\"), String.class);\n\n        // 查询所有班级名称\n        // select class_name from t_classes\n        classesDao.queryList(new Q(\"className\"), String.class);\n        classesDao.queryList(new LambdaQuery\u003c\u003e(ClassesPO::getClassName), String.class);\n\n        // 查询年龄满18的学生\n        // select * from t_student where age \u003e= 18\n        studentDao.queryList(new Q().gte(\"age\", 18));\n        studentDao.queryList(new LambdaQuery\u003cStudentPO\u003e().gte(StudentPO::getAge, 18));\n\n        // 查询年龄在16-18之间的男学生\n        // select * from t_student where sex = 1 and age between 16 and 18\n        studentDao.queryList(new Q().eq(\"sex\", 1).between(\"age\", 16, 18));\n        studentDao.queryList(new LambdaQuery\u003cStudentPO\u003e().eq(StudentPO::getSex, 1).between(StudentPO::getAge, 16, 18));\n\n        // 查询熊姓学生\n        // select * from t_student where name like '熊%'\n        studentDao.queryList(new Q().likeRight(\"name\", \"熊\"));\n        studentDao.queryList(new LambdaQuery\u003cStudentPO\u003e().likeRight(StudentPO::getName, \"熊\"));\n\n        // 查询特殊备注的学生\n        // select sno, name, `desc` from t_student where `desc` is not null\n        studentDao.queryList(new Q(\"sno\", \"name\", \"desc\").isNotNull(\"desc\"));\n        studentDao.queryList(new LambdaQuery\u003c\u003e(StudentPO::getSno, StudentPO::getName, StudentPO::getDesc).isNotNull(StudentPO::getDesc));\n\n        // 查询年龄最大学生\n        // select * from t_student order by age desc limit 1\n        studentDao.query(new Q().desc(\"age\").page(1, 1));\n\n        // 查询学生人数和平均年龄\n        // select count(1) as count, avg(age) as avgAge from t_student\n        studentDao.queryForMap(new Q()\n                .selectExpression(\"count(1)\", \"count\")\n                .selectExpression(\"avg(${age})\", \"avgAge\")\n        );\n\n        // 统计学生性别人数小于5的性别及人数\n        // select sex, count(id) as count from t_student group by sex having count \u003c 5\n        studentDao.queryForMap(new Q()\n                .select(\"sex\")\n                .selectExpression(\"count(id)\", \"count\")\n                .groupBy(\"sex\")\n                .having(Filter.lt(\"count\", 5))\n        );\n\n        // 查询小于平均年龄的女学生\n        // select * from t_student where sex = 2 and age \u003c (select avg(age) from t_student)\n        studentDao.queryList(new Q()\n                .eq(\"sex\", 2)\n                .filterSubQuery(\"age\", Operator.LT, SubQueryParam.build(\n                        new Q().selectExpression(\"avg(age)\", null)\n                ))\n        );\n\n        // 查询姓名里存在“熊”或者“小”的男学生\n        // select * from t_student where sex = 1 and (name like '%熊%' or name like '%小%')\n        studentDao.queryList(new Q()\n                .eq(\"sex\", 1)\n                .andBracket(Filter.like(\"name\", \"熊\"), Filter.or(), Filter.like(\"name\", \"小\"))\n        );\n\n        // 分页查询学生（第二页）\n        // select * from t_student limit 10, 10\n        studentDao.queryPage(new Q().page(2, 10));\n\n        // 查询空表\n        // select 1 from dual\n        studentDao.queryForType(new EQ(new TableEntity(\"dual\")).select(\"1\"), Integer.class);\n\n        // 无表查询\n        // select 1\n        studentDao.queryForType(new EQ(new TableEntity(null)).select(\"1\"), Integer.class);\n    }\n\n}\n```\n### 关联查询示例\n```java\npublic class JoinQueryTest extends BaseTest {\n\n    public static void main(String[] args) {\n        // 查询学生及所在班级\n        // select s.*, c.class_name from t_student s join t_classes c on s.class_id = c.id\n        EQ student = new EQ(StudentPO.class, \"s\");\n        student.join(new EQ(ClassesPO.class, \"c\").select(\"className\")).on(\"classId\", \"id\");\n        student.selectAll();\n        studentDao.queryList(student, StudentVO.class);\n\n        // lambda\n        LambdaEntityQuery\u003cStudentPO\u003e lambdaStudent = new LambdaEntityQuery\u003c\u003e(StudentPO.class, \"s\");\n        lambdaStudent.join(new LambdaEntityQuery\u003c\u003e(ClassesPO.class, \"c\").select(ClassesPO::getClassName)).on(StudentPO::getClassId, ClassesPO::getId);\n        lambdaStudent.selectAll();\n        studentDao.queryList(lambdaStudent, StudentVO.class);\n\n        // 查询平均分及格的所有课程\n        // select c.*, avg(s.score) as avgScore from t_course c join t_score s on c.id = s.course_id having avgScore \u003e= 60\n        EQ c = new EQ(CoursePO.class, \"c\");\n        c.join(new EQ(ScorePO.class, \"s\")).on(\"id\", \"courseId\");\n        c.selectAll().selectExpression(\"avg(s.score)\", \"avgScore\");\n        c.having(Filter.gte(\"avgScore\", 60));\n        courseDao.queryList(c, CourseScoreVO.class);\n\n        // 查询 “初一一班” 班主任及所有任课老师\n        // select t.* from t_classes c\n        // inner join t_teacher t on c.master_tno = t.tno\n        // where c.class_name = '初一一班'\n        // union\n        // select distinct t.* from t_classes c\n        // inner join t_course course on c.id = course.class_id\n        // inner join t_teacher t on course.tno = t.tno\n        // where c.class_name = '初一一班'\n        EQ classes1 = new EQ(ClassesPO.class, \"c\");\n        EQ teacher1 = new EQ(TeacherPO.class, \"t\");\n        classes1.join(teacher1).on(\"masterTno\", \"tno\");\n        classes1.eq(\"className\", \"初一一班\");\n        teacher1.selectAll();\n\n        EQ classes2 = new EQ(ClassesPO.class, \"c\");\n        EQ course = new EQ(CoursePO.class, \"course\");\n        EQ teacher2 = new EQ(TeacherPO.class, \"t\");\n        classes2.join(course).on(\"id\", \"classId\");\n        course.join(teacher2).on(\"tno\", \"tno\");\n        classes2.eq(\"className\", \"初一一班\");\n        teacher2.selectAll();\n\n        classes1.union(classes2.distinct());\n        classesDao.queryList(classes1, TeacherPO.class);\n\n        // 查询学生信息及每门课程对应的分数和所在班级、课程对应的任课老师，学生分数打上标签：及格/不及格\n        // select s.sno, s.name, c.class_name, course.course_name, t.name as courseTeacher, score.score, if(score.score\u003e=60, '及格', '不及格') as scoreLabel\n        // from t_student s\n        // join t_classes c on s.class_id = c.id\n        // join t_score score on s.sno = score.sno\n        // join t_course course on score.course_id = course.id\n        // join t_teacher t on course.tno = t.tno\n        EQ _student = new EQ(StudentPO.class, \"s\");\n        _student.join(new EQ(ClassesPO.class, \"c\")).on(\"classId\", \"id\");\n        EQ _score = new EQ(ScorePO.class, \"score\");\n        _student.join(_score).on(\"sno\", \"sno\");\n        EQ _course = new EQ(CoursePO.class, \"course\");\n        _score.join(_course).on(\"courseId\", \"id\");\n        _course.join(new EQ(TeacherPO.class, \"t\")).on(\"tno\", \"tno\");\n\n        _student.selects(\"sno\", \"name\", \"c.className\", \"course.courseName\", \"score.score\");\n        _student.select(\"t.name\", \"courseTeacher\");\n        _student.selectExpression(\"if(score.score\u003e=60, '及格', '不及格')\", \"scoreLabel\");\n\n        studentDao.queryList(_student, StudentCourseScoreVO.class);\n    }\n\n}\n```\n### 子查询示例\n```java\npublic class SubQueryTest extends BaseTest {\n\n    public static void main(String[] args) {\n        // 简单子查询\n        // select count(id) from (select * from t_student) t\n        studentDao.queryForType(new SubQuery(new Q(), \"t\").selectExpression(\"count(id)\", \"count\"), Integer.class);\n\n        // 通过子查询关联查询小于平均年龄的男同学\n        // select s.* from t_student s join (select avg(age) as age from t_student) t on s.age \u003c t.age where s.sex = 2\n        EQ student = new EQ(StudentPO.class, \"s\");\n        student.join(\n                new SubQuery(new Q().selectExpression(\"avg(age)\", \"age\"), \"t\")\n        ).on(Filter.expression(\"s.age\", Operator.LT, \"t.age\"));\n        student.selectAll().eq(\"sex\", 2);\n        studentDao.queryList(student);\n\n        // 通过子查询查询 “初一一班” 班主任及所有任课老师\n        // select * from t_teacher where\n        // tno in (select master_tno from t_classes where class_name = '初一一班')\n        // or\n        // tno in (select course.tno from t_classes c inner join t_course course on c.id = course.class_id where c.class_name = '初一一班')\n        EntityQuery subQuery1 = new EQ(ClassesPO.class).select(\"masterTno\").eq(\"className\", \"初一一班\");\n        EntityQuery subQuery2 = new EQ(ClassesPO.class, \"c\").eq(\"className\", \"初一一班\");\n        subQuery2.join(new EQ(CoursePO.class, \"course\").select(\"tno\")).on(\"id\", \"classId\");\n        teacherDao.queryList(new Q()\n                .filterSubQuery(\"tno\", Operator.IN, SubQueryParam.build(subQuery1))\n                .addFilters(Filter.or())\n                .filterSubQuery(\"tno\", Operator.IN, SubQueryParam.build(subQuery2))\n        );\n\n        // 通过子查询来查询学生班级名称\n        // select s.*, (select class_name from t_classes where id = s.class_id) as className from t_student\n        studentDao.queryList(new EQ(StudentPO.class, \"s\")\n                .selectAll()\n                .selectSubQuery(SubQueryParam.build(\n                        new EQ(ClassesPO.class)\n                                .select(\"className\")\n                                .filterExpression(\"id\", Operator.EQ, \"${s.classId}\")\n                )), StudentVO.class\n        );\n\n        /*\n        // 模拟 oracle 分页\n        // 等价于 studentDao.queryList(new Q().page(new OraclePage(1, 10)))\n        // select t.* from (select t.*, ROWNUM as row from (select * from t_student) t where ROWNUM \u003c= 10) t where t.row \u003e 0\n        int page = 1, size = 10;\n        studentDao.queryList(\n                new SubQuery(\n                        new SubQuery(new Q(), \"t\")\n                                .selectAll()\n                                .realSelect(\"ROWNUM\", \"row\")\n                                .realFilter(Filter.lte(\"ROWNUM\", page * size)),\n                        \"t\"\n                ).gt(\"row\", page * size - size)\n        );\n        */\n    }\n\n}\n```\n\n### With查询示例\n```java\npublic class WithQueryTest extends BaseTest {\n\n    public static void main(String[] args) {\n        // 通过 with 联合查询\n        // with area1 as (select * from t_area where level = 1),\n        // area2 as (select * from t_area where level = 2)\n        // select * from area1\n        // union all\n        // select * from area2\n        EntityQuery entityQuery = With.build()\n                .with(new EQ(AreaPO.class).eq(\"level\", 1), \"area1\")\n                .with(new EQ(AreaPO.class).eq(\"level\", 2), \"area2\")\n                .buildQuery(\n                        new EQ(new TableEntity(\"area1\")).unionAll(new EQ(new TableEntity(\"area2\")))\n                );\n        areaDao.queryList(entityQuery);\n\n        // 通过 with 递归查询\n        // with recursive area as (\n        //   select * from t_area where code = '310000'\n        //   union all\n        //   select t1.* from t_area t1 join area t2 on t1.parent_code = t2.code\n        // )\n        // select * from area\n        EntityQuery t1 = new EQ(AreaPO.class, \"t1\");\n        EntityQuery t2 = new EQ(new TableEntity(\"area\"), \"t2\");\n        t1.join(t2).on(\"parentCode\", \"code\");\n        t1.selectAll();\n\n        EntityQuery entityQuery2 = With.buildRecursive()\n                .with(new EQ(AreaPO.class).eq(\"code\", \"310000\").unionAll(t1), \"area\")\n                .buildQuery(new EQ(new TableEntity(\"area\")));\n        areaDao.queryList(entityQuery2);\n    }\n\n}\n```\n\n### 新增示例\n```java\npublic class InsertTest extends BaseTest {\n\n    public static void main(String[] args) {\n\n        // 新增一个学生\n        // insert into t_student(...) values (...)\n        studentDao.add(getStudent());\n\n        // 批量新增学生\n        // insert into t_student(...) values (...), (...), (...)\n        studentDao.batchAdd(Arrays.asList(getStudent(), getStudent(), getStudent()));\n\n        // insert select 新增学生\n        // insert into t_student(...) select ... from student order by id desc limit 1\n        Long[] ids = studentDao.batchAdd(new BatchEntityInsert(\n                new EQ(StudentPO.class)\n                        .selects(\"age\", \"sex\", \"version\", \"isDeleted\", \"createTime\")\n                        .selectExpression(\"concat('copy_', name)\", \"name\")\n                        .selectExpression(\"concat('copy_', sno)\", \"sno\")\n                        .desc(\"id\").page(1, 1)\n        ));\n        studentDao.queryList(new Q().in(\"id\", ids));\n\n        // 防学号重复新增学生\n        // insert into t_student(...) select concat('copy_', sno), ... from t_student\n        // where sno = 's001' and not exists (select 1 from t_student where sno = 'copy_s001')\n        studentDao.batchAdd(new BatchEntityInsert(\n                new EQ(StudentPO.class)\n                        .selects(\"age\", \"sex\", \"version\", \"isDeleted\", \"createTime\")\n                        .selectExpression(\"concat('copy_', name)\", \"name\")\n                        .selectExpression(\"concat('copy_', sno)\", \"sno\")\n                        .eq(\"sno\", \"s001\")\n                        .notExists(SubQueryParam.build(new Q(\"1\").eq(\"sno\", \"copy_s001\")))\n        ));\n\n        // replace into\n        StudentPO student = getStudent();\n        student.setId(System.currentTimeMillis());\n        student.setDesc(\"replace: add\");\n        studentDao.add(new EntityInsert(student).withReplace());\n        student.setDesc(\"replace: update\");\n        studentDao.add(new EntityInsert(student).withReplace());\n    }\n\n    private static StudentPO getStudent() {\n        try {\n            Thread.sleep(5);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        long s = System.currentTimeMillis();\n        StudentPO studentPO = new StudentPO();\n        studentPO.setName(\"学生_\" + s);\n        studentPO.setSno(\"s\" + s);\n        studentPO.setAge(18);\n        studentPO.setSex(SexEnum.MALE);\n        studentPO.setClassId(1L);\n        studentPO.setIsDeleted(0L);\n        studentPO.setVersion(0);\n        studentPO.setCreateTime(new Date());\n        studentPO.setUpdateTime(new Date());\n        return studentPO;\n    }\n\n}\n```\n\n### 动态查询机制\n支持动态查询机制，可通过配置字段注解提前定义查询方式和动态关联、静态关联表。\n非常灵活的实现前端传参后端动态查询，支持后端不需要写任何代码，根据前端传参自动关联表进行各种条件的动态查询\n\u003cbr\u003e\u003cbr\u003e\n动态查询说明：\u003cbr\u003e\n前端传 { id: 1 } 自动映射成 id = 1 \u003cbr\u003e\n前端传 { id: [1, 2, 3] } 自动映射成 id in (1,2,3) \u003cbr\u003e\n前端传 { gte_age: 18 } 自动映射成 age \u003e= 18 \u003cbr\u003e\n前端传 { gt_age: 18 } 自动映射成 age \u003e 18 \u003cbr\u003e\n前端传 { lte_age: 30 } 自动映射成 age \u003c= 30 \u003cbr\u003e\n前端传 { lt_age: 30 } 自动映射成 age \u003c 30 \u003cbr\u003e\n前端传 { neq_age: 30 } 自动映射成 age != 30 \u003cbr\u003e\n前端传 { neq_age: [1, 2, 3] } 自动映射成 age not in (1,2,3) \u003cbr\u003e\n前端传 { name: '罗' } 自动映射成 name = '罗' \u003cbr\u003e\n前端传 { name: '罗%' } 自动映射成 name like '罗%' \u003cbr\u003e\n前端传 { name: '%罗%' } 自动映射成 name like '%罗%' \u003cbr\u003e\n\n动态关联说明：\u003cbr\u003e\n有一张学生表 t_student 和一张班级表 t_classes\n如果前端传了 className 字段（学生表只有 class_id 关联班级表）就会进行自动关联 t_classes 表去查询，不用写任何代码自动根据参数去动态关联表查询。\u003cbr\u003e\n前端传 { id: 1 } 自动映射成 select * from t_student where id = 1 \u003cbr\u003e\n前端传 { id: 1, className: '三年二班' } 自动映射成 select s.* from t_student s join t_classes c on s.class_id = c.id where s.id = 1 and c.name = '三年二班' \u003cbr\u003e\n\n具体参考单元测试 cn.veasion.db.criteria.QueryCriteriaTest\n\n### 拦截器\n自定义拦截器可继承抽象类 cn.veasion.db.interceptor.AbstractInterceptor\u003cbr\u003e\n\n内置：逻辑删除拦截器 cn.veasion.db.interceptor.LogicDeleteInterceptor \u003cbr\u003e\n内置：拒绝无条件修改删除拦截器 cn.veasion.db.interceptor.UpdateDeleteNoFilterInterceptor \u003cbr\u003e\n\n其他如租户SaaS数据隔离拦截器实现见单元测试：cn.veasion.db.interceptor.TenantInterceptor \u003cbr\u003e\n\n需要使用上面拦截器功能可在项目 resources/META-INF/services 目录下新建 SPI 文件 cn.veasion.db.interceptor.EntityDaoInterceptor 中加入指定拦截器类，或显性调用InterceptorUtils.addInterceptor方法添加\u003cbr\u003e\n\n具体参考单元测试代码示例。\n\n### 类型转换\n框架默认支持基本数据类型转换，其他类型可自定义扩展，SPI 实现 cn.veasion.db.utils.TypeConvert 接口\u003cbr\u003e\n\n示例：枚举转换扩展见单元测试 cn.veasion.db.interceptor.ExtTypeConvert\n\n### 动态表名\n见 cn.veasion.db.jdbc.DefaultDynamicTableExt 加入SPI支持。\u003cbr\u003e\n\n具体参考单元测试：cn.veasion.db.table.DynamicTableTest\n\n### spring 项目接入 veasion-db\n推荐使用基础框架 [veasion-project-base](https://github.com/veasion/veasion-project-base) 接入\n\nSPI 实现 cn.veasion.db.jdbc.DataSourceProvider 接口\n```java\npublic class DefaultDataSourceProvider implements DataSourceProvider {\n\n    @Override\n    public DataSource getDataSource(EntityDao\u003c?, ?\u003e entityDao, JdbcTypeEnum jdbcTypeEnum) {\n        // 可已定义根据 jdbcTypeEnum 判断读写类型，获取不同数据源\n        // SpringUtils 是获取 bean 的工具类（自写）\n        return SpringUtils.getBean(DataSource.class);\n    }\n\n    @Override\n    public Connection getConnection(DataSource dataSource) throws SQLException {\n        return org.springframework.jdbc.datasource.DataSourceUtils.getConnection(dataSource);\n    }\n\n    @Override\n    public void releaseConnection(DataSource dataSource, Connection connection) {\n        return org.springframework.jdbc.datasource.DataSourceUtils.releaseConnection(connection, dataSource);\n    }\n}\n```\n\n### 适配 spring-mybatis\n在 springboot / spring 中适配 veasion-db 和 mybatis 共存，见项目 [veasion-db-mybatis](https://github.com/veasion/veasion-db-mybatis)\n\n### 基础封装\n基于 springboot 基础封装见项目 [veasion-project-base](https://github.com/veasion/veasion-project-base)\n\n## 赞助\n\n项目的发展离不开您的支持，请作者喝杯咖啡吧~\n\n![支付宝](https://veasion.oss-cn-shanghai.aliyuncs.com/alipay.png?x-oss-process=image/resize,m_lfit,h_360,w_360)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fveasion%2Fveasion-db","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fveasion%2Fveasion-db","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fveasion%2Fveasion-db/lists"}