# tisimple **Repository Path**: oeoe/tisimple ## Basic Information - **Project Name**: tisimple - **Description**: 一个ORM微框架,一个JDBC小工具。 面向域的操作风格。 单表零SQL增删改查与分页。 安全的字符串SQL模版引擎,简单直观高效。 只需在数据库创建一张表,填入一行数据,少量配置,一键生成实体。 还有什么?....先来试试看....从建一张表开始,填一行数据,.... - **Primary Language**: Java - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: https://gitee.com/oeoe/tisimple - **GVP Project**: No ## Statistics - **Stars**: 7 - **Forks**: 1 - **Created**: 2022-01-02 - **Last Updated**: 2022-05-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: Java, ORM, MyBatis, Hibernate ## README # TiSimple 越简单越好用,越直接越高效。封装JDBC的SQL执行框架,一款简单的ORM微框架。 #### MANIFESTO > ideas gathering.... > #### 简介 > 一个轻ORM框架,一个JDBC小工具 > > 面向域的操作风格 > > 单表零SQL增删改查与分页 > > 安全的字符串SQL模版引擎,简单直观高效可扩展 > > 只需在数据库创建一张表,填入一行数据,少量配置,一键生成实体 > > 还有什么?....先来试试看....从建一张表开始,填一行数据,.... #### 使用要求 1. JDK 1.8+ 2. 实体类属性若为基本类型,则必须为包装类型 3. 如果要使用 IBaseAbility 的sql能力,请务必使用@TiTable指明表名 #### 着重注意 1. 默认实体属性名与对应表的字段名是相同的,若不同,需要使用@TiColumn指定表字段名,建议使用生成工具自动生成 2. 实体类类型与数据库字段类型需绝对对应 3. 从table生成实体是有条件的,需要表中至少有一条记录,且各个字段不为空,这是为了让生成的实体属性类似与字段类型绝对对应。 #### 使用步骤 1. 配置数据源以及连接池,可以使用任意一种连接池 2. 将连接池注册到 TiDBPoolManager 3. 通过 TiDBPoolManager 获取连接,即可使用TiSimpleCore中各个方法。 4. 如果要支持分页,使用TiGlobalConfig注册全局分页工厂,也可以每个实体指定设置一个分页工厂 5. 详细的例子,请看示例项目example-single和example-spring #### 安装教程 1. clone TiSimple 项目后执行 maven的install将它安装到本地 2. 在项目中引入它 3. 可以查看示例项目 example-single 4. 之后可能会提交到Maven.... #### 求关注+求三连 > 如果觉得还可以,您的点赞与star是我继续完善它并坚持的动力。 #### 在线API文档 >https://apidoc.gitee.com/oeoe/tisimple #### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request ## 使用教程 > 使实体继承TiEntity.class > > 在类名上添加注释@TiTable,并申明表名 > > 如果太麻烦,使用 EntityBuilderFactory 生成实体,只需要定义好表,填入一行数据... > > 如果还太麻烦了,稍等等...,稍后再码一个更好的工具,expecting.... --- --- ### 使用概要 > 使用之前 > > > 需要使用TiDBPoolManager注册数据源,也可以通过TiGlobalConfig配置全局分页工厂 > > 实体使用: > > > 实体可以通过自带的工具一键生成 > > > 新建一个实体,继承TiEntity类,配置注解@TiColumn和@TiTable即可使用CRUD接口 > > > CUD接口中,接口名分别为,insert,update,delete;各有无参,字符串SQL参,模版参等三种不同接口 > > > R接口有,query,queryOne,queryList,queryMap,onlyOne,page,count等几种接口 > > > count接口,用于统计记录数 > > > 如果是多表查询,你需要新建一个结果实体继承TiEntity类,再通过SqlFactory创建模版sql语句,并通过这个结果实体query接口执行; > > 如果你只想返回一个map对象,可以通过TiSimpleCore执行模版sql > > > 若需要使用实体的分页,需要注册分页工厂或先注册全局分页工厂 > > > 若要新增数据时自动生成主键,需要在@TiColumn中指定主键生成策略 > > > 若要新增数据时自动校验非空,需要在@TiColumn中指定字段非空 > > > 若要更改字段的默认匹配方式,需要在@TiColumn中指定匹配策略 > > 核心使用: > > > 第一步,通过SqlFactory创建模版sql > > > 第二步,通过TiSimpleCore的相关接口执行模版sql,并将结果转换为实体 > ### 配置 ```java package com.example.tisimple; import com.gitee.oeoe.tisimple.core.TiDBPoolManager; import com.gitee.oeoe.tisimple.core.TiGlobalConfig; import com.gitee.oeoe.tisimple.page.mysql.MysqlPageFactory; import com.gitee.oeoe.tisimple.template.SqlFactory; import com.gitee.oeoe.tisimple.template.fun.ForeachFunc; import com.mysql.cj.jdbc.MysqlDataSource; /** * 只需要创建任何一个连接池,注册到TiDBPoolManager即可 */ public class TiConfig { public static final String url = "jdbc:mysql://localhost:3306/alis"; public static final String username = "mime"; public static final String password = "123"; public static final String driver = "com.mysql.cj.jdbc.Driver"; static { try { Class.forName(driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setURL(url); dataSource.setUser(username); dataSource.setPassword(password); TiGlobalConfig.setGlobalPageFactory(MysqlPageFactory.class, 5);//注册全局分页工厂,设置默认分页大小 TiDBPoolManager.register(dataSource);//注册连接池 SqlFactory.registerFunc("foreach", ForeachFunc.class);//如果有自定义模版函数,注册模版函数 } public static long id() { return System.nanoTime(); } } ``` ### 快速入门 > 更多使用方式和配置,请看示例项目example-single和example-spring ```java package com.example.tisimple.advance; import com.example.tisimple.TiConfig; import com.example.tisimple.domain.User; import com.gitee.oeoe.tisimple.page.IPage; import org.junit.jupiter.api.Test; import java.util.LinkedList; import java.util.List; /** * 基本功能演示 */ public class PrimerTest { static { TiConfig.id(); } @Test void insert() { User user = new User(); user.setName("测试1"); user.setId(null);//id是int类型的自增,所以这里可以不用设置,可注释掉 user.setAge(9); user.setAddress("china"); //任何null的属性都将被忽略 int insert = user.insert(); System.out.println(user); assert insert == 1 : "insert 失败"; } @Test void insertBatch() { List users = new LinkedList<>();//使用linkedList for (int i = 0; i < 100; i++) { User user = new User(); user.setName("测试batch" + i); user.setId(null);//id是int类型的自增,所以这里可以不用设置,可注释掉 user.setAge(i); user.setAddress("china"); users.add(user); } User user = new User(); int insert = user.insert(users); System.out.println("batch insert rows:" + insert); System.out.println("look for:\n" + users);//自增id将会被自动设置 } @Test void delete() { User condition = new User(); condition.setName("测试1"); int delete = condition.delete(); System.out.println("delete rows:" + delete); } @Test void update() { User condition = new User(); condition.setName("测试1"); User up = new User(); up.setAge(99); up.setAddress("earth"); System.out.println("update before:" + condition.query()); int update = condition.update(up); System.out.println("update rows:" + update); System.out.println("update after:" + condition.query()); } @Test void page() { //分页功能必须在注册分页工厂后才能使用,否则将返回null User condition = new User(); //设置查询条件 condition.setAddress("china"); //本身有默认参数,也可设置分页参数,默认参数在注册分页工厂的时候设置 condition.pageSize(10);//分页大小 condition.pageIndex(1);//当前分页索引 IPage page = condition.page(); System.out.println("page result:\n" + page); } @Test void query() { User condition = new User(); condition.setName("测试1"); List query = condition.query(); System.out.println("query result:\n" + query); } @Test void onlyOne() { //查询一条记录,如果返回多条,将抛出异常 //一般用于主键查询 User condition = new User(); condition.setName("测试1"); User one = condition.onlyOne(); System.out.println("query result:\n" + one); } @Test void count() { User condition = new User(); condition.setAddress("china"); Long count = condition.count(); System.out.println("count result:\n" + count); } } ``` ### 进阶示例 ```java package com.example.tisimple.advance; import com.example.tisimple.TiConfig; import com.example.tisimple.domain.Example; import com.example.tisimple.domain.Order; import com.gitee.oeoe.tisimple.page.IPage; import com.gitee.oeoe.tisimple.page.mysql.MysqlPageFactory; import com.gitee.oeoe.tisimple.template.ISQL; import com.gitee.oeoe.tisimple.template.SqlFactory; import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; /** * 基于实体的进阶用法示例 */ class AdvanceTest { public static final Long serialVersionUID = TiConfig.id(); @Test void insert() { Order order = new Order(); order.setOrderTime(LocalDateTime.now()); order.setStatus(1); order.setPayment("zfb"); order.setTotalMoney(BigDecimal.valueOf(335)); order.setActualAmountPaid(BigDecimal.valueOf(236.23)); order.setUserId("1"); order.setUserName("爱德华"); int insert = order.insert(); System.out.println(insert); System.out.println("=============也可以指定主键自动生成工厂============="); //在id 列的@TiColumn注解申明属性keyGenerateFactory 指定主键生成工厂UUIDFactory.class //使用主键自动生成,看一下生成的主键 if (1 == insert) { System.out.println(order.getId()); } //自定义sql,对于单表的增删改,不建议使用自定义的sql,提供这些接口只是方便在某些极端业务下有可用的处理方式 String sql = "insert into k_order(id,status,user_id) values({id},{status},{userId})"; order.setId("1234567a"); int insert1 = order.insert(sql); System.out.println(insert1); } @Test void delete() { Order condition = new Order(); condition.setStatus(5); System.out.println(condition.delete()); //如果默认条件匹配不符合,也可以自定义删除sql System.out.println("=============如果默认条件匹配不符合,也可以自定义删除sql==============="); condition.setOrderTime(LocalDateTime.now()); String sql = "delete from k_order where order_time>{orderTime}"; System.out.println(condition.delete(sql)); } @Test void update() { Order condition = new Order(); condition.setId("12"); Order order = new Order(); order.setUserName("abc"); System.out.println(condition.update(order)); //与删除,都可以自定义更新sql System.out.println("========自定义更新SQL=========="); //比如需要多表更新 condition.setOrderTime(LocalDateTime.now()); String sql = "update k_order,s_user set k_order.user_name=s_user.name where k_order.user_id=s_user.id and k_order.order_time>{orderTime}"; System.out.println(condition.update(sql)); //如果你需要添加额外的参数,可以这样写 System.out.println("========多表更新=========="); ArrayList statusList = new ArrayList<>(); statusList.add(1); statusList.add(2); statusList.add(3); sql = "update k_order,s_user set k_order.user_name=s_user.name where k_order.user_id=s_user.id and k_order.order_time>{orderTime} and status in [foreach@list#(|,|)]"; ISQL template = SqlFactory.sql(sql, condition).addParam("list", statusList);//statusList是一个列表,或是其他值 System.out.println(condition.update(template)); } @Test void query() { Order condition = new Order(); List list = condition.query(); //设置查询条件值,默认所有字段匹配类型为等于 condition.setUserId("1"); System.out.println(condition.query()); System.out.println(list); //如果默认的不能满足要求,也可以拼写自己的查询sql System.out.println("=============指定sql语句=================="); String sql = "select id,status from k_order where order_time<{orderTime}"; condition.setOrderTime(LocalDateTime.now()); System.out.println(condition.queryList(sql)); System.out.println("=============更改字段的默认匹配方式=================="); //如果觉得对order_time字段的匹配方式所有地方都应该是小于,那就在Order.class 中的此字段上设置注解属性 fieldMatchTypeFactory // @TiColumn(value="order_time",comment="下单时间",fieldMatchTypeFactory = LeTypeFactory.class) // private java.time.LocalDateTime orderTime; System.out.println(condition.query()); //如果发现Order中的属性都不满足自己的条件,需要额外添加条件 System.out.println("==========额外添加参数到参数空间=========="); ArrayList statusList = new ArrayList<>(); statusList.add(1); statusList.add(2); statusList.add(3); sql = "select id,status from k_order where status in [foreach@statusList#(|,|)]"; //一种更好的写法,select id,status from k_order [foreach@statusList#where status in(|,|)] ISQL template = SqlFactory.sql(sql, condition);//先添加Order对象到参数空间 template.addParam("statusList", statusList);//再添加一个List参数到参数空间 System.out.println(condition.queryList(template)); //如果一个要查询多个表中的字段并返回,那么只需要新建一个实体包含这些字段,字段若是基本类型,必须用包装类,继承类TiEntity.class即可 System.out.println("==========多表查询,返回的字段来自多个表=========="); //所有继承类TiEntity.class的实体类都必须是一个public申明的类,意味着它不能是内部类,内部静态类 Example example = new Example(); sql = "select a.age,a.name as user_name,b.id as order_id from k_order b,s_user a where a.id=b.user_id and order_time<{orderTime}"; ISQL template1 = SqlFactory.sql(sql, condition); System.out.println(example.queryList(template1)); } @Test void page() { Order condition = new Order(); IPage page = condition.page(); System.out.println(page); System.out.println("========设置分页参数========="); //也可以手动设置分页大小 condition.pageSize(2); condition.pageIndex(1); System.out.println(condition.page()); System.out.println("========设置自己的分页工厂========="); //如果你有自己的分页工厂,也可以指定此次使用的分页工厂 MysqlPageFactory factory = new MysqlPageFactory(); //也可以设置默认的分页大小 factory.setDefaultPageSize(5); condition.setPageFactory(factory); System.out.println(condition.page()); System.out.println("=========使用自己的分页查询SQL==========="); //如果觉得默认的分页查询方式不满足要求,也可以手动书写查询SQL String sql = "select id,status from k_order where order_time<{orderTime}"; condition.setOrderTime(LocalDateTime.now()); System.out.println(condition.page(sql)); } @Test void count() { Order condition = new Order(); System.out.println(condition.count()); System.out.println("========使用自己的统计方式============"); //如果默认的统计方式不符合要求,可以使用自己的统计sql condition.setOrderTime(LocalDateTime.now()); String sql = "select count(*)as total from k_order b,s_user a where a.id=b.user_id and order_time<{orderTime}"; System.out.println(condition.queryCount(sql, "total")); //也可以是这样的 System.out.println("===========也可以是这样的==============="); ArrayList statusList = new ArrayList<>(); statusList.add(1); statusList.add(2); statusList.add(3); sql = "select count(*) as sum from k_order where status in [foreach@list#(|,|)]"; ISQL template = SqlFactory.sql(sql, statusList);//sqlL()允许参数是一个list,会在参数空间给予默认参数名list //如果你还需要Order对象中的参数作为条件,可以这样写 SqlTemplate.sqlO(sql, condition).addParam("list", statusList) System.out.println(condition.queryCount(template, "sum"));//sum 为count(*)的别名 } @Test void queryList() { Order condition = new Order(); String sql = "select id,status from k_order where order_time<{orderTime}"; condition.setOrderTime(LocalDateTime.now()); List objects = condition.queryList(sql); System.out.println(objects); } @Test void queryOne() { } @Test void queryCount() { } @Test void testPage() { } } ``` ### 模版语法 > 第一类:?占位符形式,使用 '?' 占位参数,参数将按照顺序填入 > > 第二类:{1}序列编号形式,使用 '{number}' ,number指定参数的位置 > > 第三类:命名参数形式,使用 '{varName}' 指定参数 > > 示例如下: ```java package com.example.tisimple.advance; import com.example.tisimple.domain.User; import com.gitee.oeoe.tisimple.core.TiSimpleCore; import com.gitee.oeoe.tisimple.exception.SqlInjectionException; import com.gitee.oeoe.tisimple.template.ISQL; import com.gitee.oeoe.tisimple.template.SqlFactory; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.HashMap; /** * 模版用法 */ public class TemplateUseTest { /** * 无参sql */ @Test public void zero() { String sql = "select * from s_user where id=1 and age=18 and address='china'"; ISQL template = SqlFactory.sql(sql); //解析后的sql与原sql没有变化 System.out.println("parsed SQL:" + template.getParsedSql()); } /** * ?占位符 */ @Test public void one() { String sql = "select * from s_user where id=? and age=? and address=?"; ISQL template = SqlFactory.sql(sql, 1, 18, "china"); //解析后的sql与原sql没有变化 System.out.println("parsed SQL:" + template.getParsedSql()); //组合参数后的参考sql:select * from s_user where id='1' and age='18' and address='china' System.out.println("refer SQL:" + template.getReferSql()); } /** * {1}序列编号 */ @Test public void two() { String sql = "select * from user where id={2} and age={3} and address={1} "; //{2}对应的是第二个参数位置上的参数,花括号中的数字代表者参数位置,从1开始。 ISQL template = SqlFactory.sql(sql, "china", 1, 18); //解析后的sql将变成?占位符形式的sql,select * from s_user where id=? and age=? and address=? System.out.println("parsed SQL:" + template.getParsedSql()); //组合参数后的参考sql:select * from s_user where id='1' and age='18' and address='china' System.out.println("refer SQL:" + template.getReferSql()); } /** * {name}命名参数-map对象 */ @Test public void three() { String sql = "select * from user where id={id} and age={age} and address={address} "; HashMap param = new HashMap<>(); param.put("id", 1); param.put("age", 18); param.put("address", "china"); //花括号中的名称即参数名称,是map对象中key ISQL template = SqlFactory.sql(sql, param); //如果觉得创建map太麻烦,可以直接向ISQL中添加参数,如下: template.addParam("id", 1) .addParam("age", 18) .addParam("address", "china"); //解析后的sql将变成?占位符形式的sql,select * from s_user where id=? and age=? and address=? System.out.println("parsed SQL:" + template.getParsedSql()); //组合参数后的参考sql:select * from s_user where id='1' and age='18' and address='china' System.out.println("refer SQL:" + template.getReferSql()); } /** * {name}命名参数-实体对象 */ @Test public void four() { String sql = "select * from user where id={id} and age={age} and address={address} "; User user = new User(); user.setId(1); user.setAge(18); user.setAddress("china"); //花括号中的命名参数名称必须要与实体中的字段一致,默认所有的命名参数的值为字符串null ISQL template = SqlFactory.sql(sql, user); //解析后的sql将变成?占位符形式的sql,select * from s_user where id=? and age=? and address=? System.out.println("parsed SQL:" + template.getParsedSql()); //组合参数后的参考sql:select * from s_user where id='1' and age='18' and address='china' System.out.println("refer SQL:" + template.getReferSql()); } /** * {name}命名参数-list对象 */ @Test public void five() { //[foreach@list#where id in(|,|)] 是foreach模版函数的用法,一般用于遍历集合,生成in 类型的sql String sql = "select * from user [foreach@list#where id in(|,|)]"; ArrayList ids = new ArrayList<>(); ids.add(1); ids.add(2); ids.add(3); //参数是collection类型,将会默认给予一个叫list的参数 ISQL template = SqlFactory.sql(sql, ids); //它等同于下面的方式 template.addParam("list", ids); //解析后的sql将变成?占位符形式的sql,select * from user where id in(?,?,?) System.out.println("parsed SQL:" + template.getParsedSql()); //组合参数后的参考sql:select * from user where id in('1','2','3') System.out.println("refer SQL:" + template.getReferSql()); } /** * {#name}特殊命名参数-命名参数以#开头,进行直接替换,不会生成预编译语句 */ @Test public void six() throws SqlInjectionException { String sql = "select * from user where id={#id} and age={#age} and address={#address} "; User user = new User(); user.setId(1); user.setAge(18); user.setAddress("'china'"); //如果花括号中的命名参数名称前以#开头,将会进行直接替换,不会生成预编译语句 ISQL template = SqlFactory.sql(sql, user); //这种方式存在sql注入风险,可以调用下面的方法进行检查 TiSimpleCore.checkSqlInjection(user.getAddress()); //解析后的sql将变成,select * from user where id=1 and age=18 and address=china System.out.println("parsed SQL:" + template.getParsedSql()); //组合参数后的参考sql:select * from user where id=1 and age=18 and address=china System.out.println("refer SQL:" + template.getReferSql()); } /** * 自定义参数 */ @Test public void seven() { String sql = "select * from user where id={id}"; ISQL template = SqlFactory.sql(sql); //参数可以通过如下进行添加 template.addParam("id", 1); //也可以获取参数进行查看 Object id = template.getParam("id"); System.out.println(id); //也可以复制一个模版,这个模版和原模版是两个对象 ISQL copy = template.copy(); System.out.println(copy); //也可以保留原模版的参数空间,更改原模版的sql String newSql = "select * from user where id>{id}"; ISQL copy1 = template.copy(newSql); System.out.println(copy1); } /** * 限制的存储过程 */ @Test public void eight() { ISQL template = SqlFactory.sql("{call getOrderById3(?,?)}", 1, "爱德华"); //解析后的sql不变 System.out.println("parsed SQL:" + template.getParsedSql()); //组合参数后的参考sql:{call getOrderById3('1','爱德华')} System.out.println("refer SQL:" + template.getReferSql()); } } ``` ### 模版函数规则-v1.0.1 > 模版函数调用规则: > *** > > 一个模版函数调用以 ***\[*** 开头,以 ***\]*** 结束,紧跟 ***\[*** 之后的funName是函数名,表示要调用的模版方法, ***@*** 与 ***#*** 之间的表示主参数,一般是参数空间中的属性, ***#*** 之后的是副参数部分,副参数只会被当做字符串处理(随着模版函数的丰富,后期可能会扩展类型),以 ***|*** 分隔为副参数列表 > *** > 目前内置模版函数: > > **null** 函数,格式:**\[null@主参数#副参数1|副参数2\]**, 如果主参数为 **对象null 或 字符度空 或 集合长度0** 则返回副参数1,否则返回副参数2 > >> select * from user **[null@name#|where name={name}]** > >>``` > >>如果name为空,整个sql为:select * from user,如果不为空,整个sql为:select * from user where name={name} > >>``` > > *** > >**foreach** 函数,格式:**[foreach@主参数#副参数1|副参数2|副参数3|副参数4\]**, 主参数为集合对象且不为空,此函数才会执行。迭代此集合对象,并拼接返回。 > > 副参数2为拼接字符串,副参数1为返回的前缀字符串,副参数3为返回的后缀字符串,参数4控制此次是直接拼接还是占位拼接,默认是占位拼接,只要此参数不为空即表示需要直接拼接。 > >> select * from user where address in **[foreach@list#(|,|)]** > >>``` > >>如果list为['大海','天空'],sql为:select * from user where address in (?,?)。这是一种安全的sql。 > >>``` > >>*** > >> select * from user where address in **[foreach@list#(|,|)|false]** > >>``` > >>如果list为['大海','天空'],sql为:select * from user where address in ('大海','天空')。这是一种不安全的sql,如果不确定list的内容, 不建议使用第四个副参数。 > >>``` > >>*** > >> select * from user **[foreach@list#where address in(|,|)]** > >>``` > >>这种写法更好,如果list为空,sql为:select * from user。 > >>``` > >>*** ### 注解 > 建议这些注解都由实体生成工具生成,keyGenerateFactory由用户指定 > @TiColumn > > 主要用于关联实体属性和表列名,如果在实体上不加此注解,默认为实体属性名即为表列名 > >*** > >设置 **fieldMatchTypeFactory** 属性,可设置字段在默认查询中的匹配规则,默认是等于(=), > > 目前可用有大于(>),大于等于(>=),小于(<),小于等于(<=),不等于(!=),全模糊匹配(like '%xxx%'),左模糊匹配(like '%xxx'),右模糊匹配(like 'xxx%')。 > >*** > >设置 **keyGenerateFactory**和 **key** 属性,可自动生成主键值,可自定义主键生成规则。 > >*** > >设置 **nullAble** 属性,申明此字段在新增的时候校验此属性是否可为null,默认true,可为空,如果设置为false,将不允许为空。 > >*** > >设置 **autoIncrement** 属性,申明此字段是否是自增字段,默认false,用于新增的时候进行非空校验与id返回设置。 > >*** >@TiTable > > 主要用于关联实体和表,如果实体不添加此注解,将无法使用基本的CRUD,即接口 ***IBaseAbility.class***中的功能无法使用。 > >*** > >设置 **wrapChar**属性,基本CRUD sql中的字段将自动加上设置的字符为前后缀, > > 在mysql中如果字段中有系统字段,字段通常需要加反引号,此时即可设置为 ' **`** '。 > >*** > ### 高级使用 > 待补充... *** #### 事务控制 > TX.open() 开启一个事务,ITxManager.join()将一个实体上的sql操作加入到一个事务中。 > > ITxManager.commit()提交事务,ITxManager.rollback()回滚事务。 > > entity.getTxManager()获取当前的事务,考虑加入还是新建,还是以非事务方式执行 > > 事务执行结束一定要执行ITxManager.close(),否则此entity对象将一直运行在事务模式,任何CUD将无效,且还会有锁表的风险,建议使用try source避免。 > > 灵活使用ITxManager.join()和entity.getTxManager()可以处理比较多的事务场景。 > > 注意,默认实现只能处理单数据源的事务。 ```java package com.example.tisimple.advance; import com.example.tisimple.TiConfig; import com.example.tisimple.domain.Order; import com.example.tisimple.domain.User; import com.gitee.oeoe.tisimple.transaction.ITxManager; import com.gitee.oeoe.tisimple.transaction.TX; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; /** * 事务使用用例 */ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class TxTest { public static final Long serialVersionUID = TiConfig.id(); /** * 事务使用示例1 */ @Test boolean txTest1() { User user = new User(); user.setName("cef"); user.setAge(888); user.setId(111000); User condition = new User(); condition.setAge(888); User up = new User(); up.setName("笑笑"); up.setAge(888); ITxManager txManager = TX.open(); try (ITxManager tx = txManager) {//try是必要的 tx.join(user).join(condition).join(up);//将在三个对象上的sql操作加入事务中 int insert = user.insert(); if (insert == 1) { //tx.commit();//如果需要和后面的操作互不影响,此处放开注释,提交一次到数据库, } else { return false; } int update = condition.update(up); if (update == 1) { tx.commit();//如果更新成功,则提交事务,在此处的commit将insert和update串联成一个事务,此insert和update也不一定是同一个表 return true; } Object o = method1(condition);//此处在事务中调用另外一个方法 return false; } catch (Exception e) {//如果期间出现任何异常,都回滚,也可根据业务判断指定的异常才回滚 e.printStackTrace(); txManager.rollback(); } return false; } public Object method1(User condition) { Order order = new Order(); order.setUserName("abc"); ITxManager txManager = condition.getTxManager();//此处获取已有的事务 //如果事务不存在,可以根据情况,是创建一个新事务,还是加入当前事务,也可以注释掉此行以非事务方式运行 txManager = txManager == null ? txManager = TX.open() : txManager.join(order); try (ITxManager tx = txManager) {//如果txManager不是新事务,则不要try source,否则会导致此方法类执行结束后关闭事务,上一级方法中sql操作失败 //... } return null; } } ``` ### 扩展 #### 模版函数 > 待补充.. > #### 分页工厂 > 待补充.. > #### 匹配规则 > 待补充.. > ### 其他 > 待补充... ### 实体生成工具的使用 > _例子使用MySQL数据库_ ```SQL -- auto-generated definition create table s_user ( id int auto_increment comment '主键' primary key, name varchar(32) null comment '用户名', age int default 0 null comment '年龄', address varchar(1024) null comment '地址' ); INSERT INTO alis.s_user (id, name, age, address) VALUES (1, '掌声', 118, '大海'); INSERT INTO alis.s_user (id, name, age, address) VALUES (2, '鲜花', 118, '山坡'); INSERT INTO alis.s_user (id, name, age, address) VALUES (3, '烈阳', 11, '天空'); INSERT INTO alis.s_user (id, name, age, address) VALUES (4, '你在哪儿', 118, '我在你身边'); INSERT INTO alis.s_user (id, name, age, address) VALUES (5, '你在哪儿', 118, '我在你身边'); INSERT INTO alis.s_user (id, name, age, address) VALUES (6, '你在哪儿', 118, '我在你身边'); INSERT INTO alis.s_user (id, name, age, address) VALUES (138430, '平方', 19, '共和国'); create table k_order ( id varchar(64) not null comment '订单号' primary key, status int null comment '订单状态,0未支付,1已支付,2待发货,3已发货,4待收货,5已完成', order_time datetime null comment '下单时间', total_money decimal null comment '订单总金额', actual_amount_paid decimal null comment '实际支付金额', payment varchar(32) null comment '支付方式', user_id varchar(64) null comment '顾客ID', user_name varchar(64) null comment '顾客昵称' ) comment '订单'; INSERT INTO alis.k_order (id, status, order_time, total_money, actual_amount_paid, payment, user_id, user_name) VALUES ('1', 1, '2022-01-12 09:59:06', 188, 100, 'wx', '1', null); INSERT INTO alis.k_order (id, status, order_time, total_money, actual_amount_paid, payment, user_id, user_name) VALUES ('b61d932caf954530897fd24524a39551', 1, '2022-01-12 10:08:59', 335, 236, 'zfb', null, null); INSERT INTO alis.k_order (id, status, order_time, total_money, actual_amount_paid, payment, user_id, user_name) VALUES ('b8da1ee90d454b2f9776f77071492774', 1, '2022-01-12 12:33:39', 335, 236, 'zfb', '1', '爱德华'); INSERT INTO alis.k_order (id, status, order_time, total_money, actual_amount_paid, payment, user_id, user_name) VALUES ('fb04c149da4d422aa963634afa696d12', 1, '2022-01-12 10:06:57', 335, 236, 'zfb', null, null); ``` > 配置 > ```java package com.example.tisimple; import com.mysql.cj.jdbc.MysqlDataSource; import we.us.tisimple.core.TiDBPoolManager; import we.us.tisimple.core.TiGlobalConfig; import we.us.tisimple.extra.pagefactory.mysql.MysqlPageFactory; /** * 只需要创建任何一个连接池,注册到TiDBPoolManager即可 */ public class TiConfig { public static final String url = "jdbc:mysql://localhost:3306/alis"; public static final String username = "mime"; public static final String password = "123"; public static final String driver = "com.mysql.cj.jdbc.Driver"; static { try { Class.forName(driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setURL(url); dataSource.setUser(username); dataSource.setPassword(password); TiDBPoolManager.register(dataSource);//注册连接池 TiGlobalConfig.setGlobalPageFactory(MysqlPageFactory.class, 5);//注册全局分页工厂,设置默认分页大小 } public static long id() { return System.nanoTime(); } } ``` *** > 代码配置生成 > ```JAVA package com.example.tisimple; import we.us.tisimple.core.TiDBPoolManager; import we.us.tisimple.entityfactory.EntityBuilderFactory; import we.us.tisimple.entityfactory.mysql.MysqlTableInfoFactory; import we.us.tisimple.exception.ConditionNotMetException; import we.us.tisimple.exception.ObtainConnectionException; import java.io.File; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 从表中生成实体 */ public class GenerateUser { public static final Long serialVersionUID = TiConfig.id(); public static void main(String[] args) throws ObtainConnectionException, ConditionNotMetException, SQLException, IOException { String path = new File("").getAbsoluteFile().getPath(); path = path + "/example-single/src/main/java/com/example/tisimple/domain/"; try (Connection connection = TiDBPoolManager.getConnection()) { PreparedStatement preparedStatement = connection.prepareStatement("select * from s_user limit 1"); ResultSet resultSet = preparedStatement.executeQuery(); EntityBuilderFactory.build() .setPackName("com.example.tisimple.domain") .setClassName("User") .setTableName("s_user") .setResultSet(resultSet) .setOutPath(path) .setInfoFactory(MysqlTableInfoFactory.class)//设置获取表信息工厂 .generate(); } } } ``` *** ```java package com.example.tisimple; import we.us.tisimple.core.TiDBPoolManager; import we.us.tisimple.entityfactory.EntityBuilderFactory; import we.us.tisimple.entityfactory.mysql.MysqlTableInfoFactory; import we.us.tisimple.exception.ConditionNotMetException; import we.us.tisimple.exception.ObtainConnectionException; import java.io.File; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 从表中生成实体 */ public class GenerateOrder { public static final Long serialVersionUID = TiConfig.id(); public static void main(String[] args) throws ObtainConnectionException, ConditionNotMetException, SQLException, IOException { String path = new File("").getAbsoluteFile().getPath(); path = path + "/example-single/src/main/java/com/example/tisimple/domain/"; try (Connection connection = TiDBPoolManager.getConnection()) { PreparedStatement preparedStatement = connection.prepareStatement("select * from k_order limit 1"); ResultSet resultSet = preparedStatement.executeQuery(); EntityBuilderFactory.build() .setPackName("com.example.tisimple.domain") .setClassName("Order") .setTableName("k_order") .setResultSet(resultSet) .setOutPath(path) .setInfoFactory(MysqlTableInfoFactory.class) .generate(); } } } ``` *** > yaml配置生成,文件名为 entity.yaml > ```yaml #项目绝对路径 rootPath: D:\idea\gitee\tisimple\example-single\src\main\java #如果配置的fileOutPath不存在,是否创建 autoCreateFileOutPath: true #默认的包名,可以为空或不写 defaultPackagePathName: com.example.tisimple.domain2 #默认的文件输出路径 defaultFileOutPath: ./com/example/tisimple/domain2 #默认的表信息获取工厂 defaultTableInfoFactoryClass: we.us.tisimple.entityfactory.mysql.MysqlTableInfoFactory #具体各个实体生成配置 details: #生成的类名 - className: User #对应的表名 tableName: s_user #指定查询一条当前表中记录的sql,这些记录的所有列都不能为空,必须有值,否则无法通过校验生成实体 selectOneSql: select * from s_user limit 1 #类的包名,可以留空或不写,则会继承上面配的默认包名 packagePathName: com.example.tisimple.domain2 #生成的文件输出路径,可以留空或不写,则会继承上面配的默认输出路径,完整路径是rootPath+fileOutPath,一定要以点开头(./) fileOutPath: ./com/example/tisimple/domain2 #表信息获取工厂,可以留空或不写,则会继承上面配的默认表信息获取工厂 tableInfoFactoryClass: we.us.tisimple.entityfactory.mysql.MysqlTableInfoFactory - className: Order tableName: k_order selectOneSql: select * from k_order limit 1 #使用默认的包名 #packagePathName: com.example.tisimple.domain2 #使用默认的文件输出路径 #fileOutPath: ./com/example/tisimple/domain2 #使用默认的表信息获取工厂 #tableInfoFactoryClass: we.us.tisimple.entityfactory.mysql.MysqlTableInfoFactory ``` ```java package com.example.tisimple.entitybuilder; import com.example.tisimple.TiConfig; import we.us.tisimple.core.TiDBPoolManager; import we.us.tisimple.entityfactory.EntityBuilderFactory; import we.us.tisimple.entityfactory.mysql.MysqlTableInfoFactory; import we.us.tisimple.exception.ConditionNotMetException; import we.us.tisimple.exception.ObtainConnectionException; import java.io.File; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 通过yaml配置,从表中生成实体 * */ public class GenerateFormYaml { public static final Long serialVersionUID = TiConfig.id(); public static void main(String[] args) { //classpath 目录下配置一个 entity.yaml 文件即可 EntityBuilderFactory.buildFromYaml(); } } ``` > 生成的文件 ```java package com.example.tisimple.domain; import we.us.tisimple.ability.TiEntity; import we.us.tisimple.annotation.TiColumn; import we.us.tisimple.annotation.TiTable; import we.us.tisimple.extra.keygenertefactory.UUIDFactory; import we.us.tisimple.extra.matchtypefactory.LeTypeFactory; @TiTable(value = "k_order") public class Order extends TiEntity { @TiColumn(value = "id", comment = "订单号", nullAble = false, key = true, keyGenerateFactory = UUIDFactory.class) private java.lang.String id; @TiColumn(value = "status", comment = "订单状态,0未支付,1已支付,2待发货,3已发货,4待收货,5已完成") private java.lang.Integer status; @TiColumn(value = "order_time", comment = "下单时间", fieldMatchTypeFactory = LeTypeFactory.class)//更改默认类型匹配 private java.time.LocalDateTime orderTime; @TiColumn(value = "total_money", comment = "订单总金额") private java.math.BigDecimal totalMoney; @TiColumn(value = "actual_amount_paid", comment = "实际支付金额") private java.math.BigDecimal actualAmountPaid; @TiColumn(value = "payment", comment = "支付方式") private java.lang.String payment; @TiColumn(value = "user_id", comment = "顾客ID") private java.lang.String userId; @TiColumn(value = "user_name", comment = "顾客昵称") private java.lang.String userName; public void setId(java.lang.String id) { this.id = id; } public java.lang.String getId() { return this.id; } public void setStatus(java.lang.Integer status) { this.status = status; } public java.lang.Integer getStatus() { return this.status; } public void setOrderTime(java.time.LocalDateTime orderTime) { this.orderTime = orderTime; } public java.time.LocalDateTime getOrderTime() { return this.orderTime; } public void setTotalMoney(java.math.BigDecimal totalMoney) { this.totalMoney = totalMoney; } public java.math.BigDecimal getTotalMoney() { return this.totalMoney; } public void setActualAmountPaid(java.math.BigDecimal actualAmountPaid) { this.actualAmountPaid = actualAmountPaid; } public java.math.BigDecimal getActualAmountPaid() { return this.actualAmountPaid; } public void setPayment(java.lang.String payment) { this.payment = payment; } public java.lang.String getPayment() { return this.payment; } public void setUserId(java.lang.String userId) { this.userId = userId; } public java.lang.String getUserId() { return this.userId; } public void setUserName(java.lang.String userName) { this.userName = userName; } public java.lang.String getUserName() { return this.userName; } @Override public String toString() { return "Order{" + "id=" + id + "," + "status=" + status + "," + "orderTime=" + orderTime + "," + "totalMoney=" + totalMoney + "," + "actualAmountPaid=" + actualAmountPaid + "," + "payment=" + payment + "," + "userId=" + userId + "," + "userName=" + userName + "}"; } } ``` *** ```java package com.example.tisimple.domain; import com.gitee.oeoe.tisimple.ability.TiEntity; import com.gitee.oeoe.tisimple.annotation.TiColumn; import com.gitee.oeoe.tisimple.annotation.TiTable; @TiTable(value = "s_user") public class User extends TiEntity { @TiColumn(value = "id", comment = "主键", nullAble = false, key = true, autoIncrement = true) private java.lang.Integer id; @TiColumn(value = "name", comment = "用户名") private java.lang.String name; @TiColumn(value = "age", comment = "年龄") private java.lang.Integer age; @TiColumn(value = "address", comment = "地址") private java.lang.String address; public void setId(java.lang.Integer id) { this.id = id; } public java.lang.Integer getId() { return this.id; } public void setName(java.lang.String name) { this.name = name; } public java.lang.String getName() { return this.name; } public void setAge(java.lang.Integer age) { this.age = age; } public java.lang.Integer getAge() { return this.age; } public void setAddress(java.lang.String address) { this.address = address; } public java.lang.String getAddress() { return this.address; } @Override public String toString() { return "User{" + "id=" + id + "," + "name=" + name + "," + "age=" + age + "," + "address=" + address + "}"; } } ``` ```java package com.example.tisimple.domain; import we.us.tisimple.ability.TiEntity; import we.us.tisimple.annotation.TiColumn; import we.us.tisimple.annotation.TiTable; @TiTable(value = "m_type", wrapChar = "`") public class MysqlType extends TiEntity { @TiColumn(value = "id", nullAble = false, key = true) private java.lang.String id; @TiColumn(value = "order", nullAble = false) private java.lang.Integer order; @TiColumn(value = "TINYINT") private java.lang.Integer tinyint; @TiColumn(value = "SMALLINT") private java.lang.Integer smallint; @TiColumn(value = "INTEGER") private java.lang.Integer integer; @TiColumn(value = "BIGINT") private java.lang.Long bigint; @TiColumn(value = "DECIMAL") private java.math.BigDecimal decimal; @TiColumn(value = "DATE") private java.sql.Date date; @TiColumn(value = "TIME") private java.lang.String time; @TiColumn(value = "YEAR") private java.sql.Date year;//year类型的对应java中date类型,是无法进行正常插入的 @TiColumn(value = "DATETIME") private java.time.LocalDateTime datetime; @TiColumn(value = "TIMESTAMP") private java.sql.Timestamp timestamp; @TiColumn(value = "CHAR") private java.lang.String char1; @TiColumn(value = "VARCHAR") private java.lang.String varchar; @TiColumn(value = "TINYBLOB") private byte[] tinyblob; @TiColumn(value = "TINYTEXT") private java.lang.String tinytext; @TiColumn(value = "BLOB") private byte[] blob; @TiColumn(value = "TEXT") private java.lang.String text; @TiColumn(value = "MEDIUMBLOB") private byte[] mediumblob; @TiColumn(value = "MEDIUMTEXT") private java.lang.String mediumtext; @TiColumn(value = "LONGBLOB") private byte[] longblob; @TiColumn(value = "LONGTEXT") private java.lang.String longtext; public void setId(java.lang.String id) { this.id = id; } public java.lang.String getId() { return this.id; } public void setOrder(java.lang.Integer order) { this.order = order; } public java.lang.Integer getOrder() { return this.order; } public void setTinyint(java.lang.Integer tinyint) { this.tinyint = tinyint; } public java.lang.Integer getTinyint() { return this.tinyint; } public void setSmallint(java.lang.Integer smallint) { this.smallint = smallint; } public java.lang.Integer getSmallint() { return this.smallint; } public void setInteger(java.lang.Integer integer) { this.integer = integer; } public java.lang.Integer getInteger() { return this.integer; } public void setBigint(java.lang.Long bigint) { this.bigint = bigint; } public java.lang.Long getBigint() { return this.bigint; } public void setDecimal(java.math.BigDecimal decimal) { this.decimal = decimal; } public java.math.BigDecimal getDecimal() { return this.decimal; } public void setDate(java.sql.Date date) { this.date = date; } public java.sql.Date getDate() { return this.date; } public void setTime(java.lang.String time) { this.time = time; } public java.lang.String getTime() { return this.time; } public void setYear(java.sql.Date year) { this.year = year; } public java.sql.Date getYear() { return this.year; } public void setDatetime(java.time.LocalDateTime datetime) { this.datetime = datetime; } public java.time.LocalDateTime getDatetime() { return this.datetime; } public void setTimestamp(java.sql.Timestamp timestamp) { this.timestamp = timestamp; } public java.sql.Timestamp getTimestamp() { return this.timestamp; } public void setChar(java.lang.String char1) { this.char1 = char1; } public java.lang.String getChar() { return this.char1; } public void setVarchar(java.lang.String varchar) { this.varchar = varchar; } public java.lang.String getVarchar() { return this.varchar; } public void setTinyblob(byte[] tinyblob) { this.tinyblob = tinyblob; } public byte[] getTinyblob() { return this.tinyblob; } public void setTinytext(java.lang.String tinytext) { this.tinytext = tinytext; } public java.lang.String getTinytext() { return this.tinytext; } public void setBlob(byte[] blob) { this.blob = blob; } public byte[] getBlob() { return this.blob; } public void setText(java.lang.String text) { this.text = text; } public java.lang.String getText() { return this.text; } public void setMediumblob(byte[] mediumblob) { this.mediumblob = mediumblob; } public byte[] getMediumblob() { return this.mediumblob; } public void setMediumtext(java.lang.String mediumtext) { this.mediumtext = mediumtext; } public java.lang.String getMediumtext() { return this.mediumtext; } public void setLongblob(byte[] longblob) { this.longblob = longblob; } public byte[] getLongblob() { return this.longblob; } public void setLongtext(java.lang.String longtext) { this.longtext = longtext; } public java.lang.String getLongtext() { return this.longtext; } @Override public String toString() { return "MysqlType{" + "id=" + id + "," + "order=" + order + "," + "tinyint=" + tinyint + "," + "smallint=" + smallint + "," + "integer=" + integer + "," + "bigint=" + bigint + "," + "decimal=" + decimal + "," + "date=" + date + "," + "time=" + time + "," + "year=" + year + "," + "datetime=" + datetime + "," + "timestamp=" + timestamp + "," + "char=" + char1 + "," + "varchar=" + varchar + "," + "tinyblob=" + tinyblob + "," + "tinytext=" + tinytext + "," + "blob=" + blob + "," + "text=" + text + "," + "mediumblob=" + mediumblob + "," + "mediumtext=" + mediumtext + "," + "longblob=" + longblob + "," + "longtext=" + longtext + "}"; } } ``` *** > 多表查询结果实体定义示例 ```java package com.example.tisimple.domain; import we.us.tisimple.ability.TiEntity; import we.us.tisimple.annotation.TiColumn; public class Example extends TiEntity { private Integer age; @TiColumn("order_id") private String orderId; @TiColumn("user_name") private String userName; @Override public String toString() { return "Example{" + "age=" + age + ", orderId='" + orderId + '\'' + ", userName='" + userName + '\'' + '}'; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } } ``` ### 更新 > 2022-1-10 文本版本 ***v1.0.0*** > > 2022-1-11 文本版本 ***v1.1.0*** > > 2022-1-12 文本版本 ***v1.1.1*** > > 2022-1-16 文本版本 ***v1.1.2*** > > 2022-1-28 文本版本 ***v1.1.3*** >