{"id":20042094,"url":"https://github.com/twogoods/tgdao","last_synced_at":"2025-05-05T08:32:29.123Z","repository":{"id":57723753,"uuid":"98528177","full_name":"twogoods/TgDao","owner":"twogoods","description":"sql generator tool based on Mybatis","archived":false,"fork":false,"pushed_at":"2017-12-16T03:14:31.000Z","size":143,"stargazers_count":30,"open_issues_count":0,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-08T19:48:16.891Z","etag":null,"topics":["annotation-processor","mybatis","mybatis-sql","mybatisgenerator"],"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/twogoods.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}},"created_at":"2017-07-27T11:29:07.000Z","updated_at":"2022-07-19T11:13:45.000Z","dependencies_parsed_at":"2022-08-24T03:20:27.284Z","dependency_job_id":null,"html_url":"https://github.com/twogoods/TgDao","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twogoods%2FTgDao","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twogoods%2FTgDao/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twogoods%2FTgDao/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twogoods%2FTgDao/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/twogoods","download_url":"https://codeload.github.com/twogoods/TgDao/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252466865,"owners_count":21752453,"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","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":["annotation-processor","mybatis","mybatis-sql","mybatisgenerator"],"created_at":"2024-11-13T10:48:50.767Z","updated_at":"2025-05-05T08:32:28.626Z","avatar_url":"https://github.com/twogoods.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 介绍\nTgDao是一款基于Mybatis的编译期SQL生成器，利用注解来表达SQL，能根据你的方法签名生成对应的Mapper.xml文件。\n它能减少你日常开发中大量简单SQL的编写，由于它只是生成Mapper.xml文件，因此对于复杂的查询场景，\n你同样可以自己编写来完成一些工具所无法生成的SQL。\n\n```\n@Table(name = \"T_User\")\npublic class User {\n    @Id(\"id\")\n    private int id;\n    private String username;\n    private int age;\n}\n\n```\n上面的model定义了模型和数据库表的关系，看到下面这些方法的签名，聪明的你肯定能猜出每个方法的sql吧，这就是这个库要做的工作。\n\n```\n@DaoGen(model = User.class)\npublic interface UserDao {\n    @Select\n    @OrderBy(\"id desc\")\n    List\u003cUser\u003e queryUser(@Condition(criterion = Criterions.EQUAL, column = \"username\") String name,\n                         @Condition(criterion = Criterions.GREATER, attach = Attach.OR) int age,\n                         @Limit int limit, @OffSet int offset);\n\n    @Select\n    List\u003cUser\u003e queryUser2(@Condition(criterion = Criterions.GREATER, column = \"age\") int min,\n                          @Condition(criterion = Criterions.LESS, column = \"age\") int max);\n\n    @Select\n    List\u003cUser\u003e queryUser3(@Condition(criterion = Criterions.EQUAL, column = \"username\") String name,\n                          @Condition(attach = Attach.OR, column = \"id\", criterion = Criterions.IN) String[] ids);\n\n    @Insert(useGeneratedKeys = true, keyProperty = \"id\")\n    int insert(User user);\n\n    @BatchInsert(columns = \"username,age\")\n    int batchInsert(List\u003cUser\u003e users);\n\n    @Update\n    @ModelConditions({\n            @ModelCondition(field = \"id\")\n    })\n    int update(User user);\n\n    @Delete\n    int delete(@Condition(criterion = Criterions.GREATER, column = \"age\") int min,\n               @Condition(criterion = Criterions.LESS, column = \"age\") int max);\n}\n```\n\n---\n\n## 文档\n引入如下依赖：\n\n```\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.twogoods\u003c/groupId\u003e\n  \u003cartifactId\u003etgdao-core\u003c/artifactId\u003e\n  \u003cversion\u003e0.1.5\u003c/version\u003e\n\u003c/dependency\u003e\n```\n### Table与Model关联\n`@Table`记录数据表的名字\n`@Id`记录主键信息\n`@Column`映射了表字段和属性的关系，如果表字段和类属性同名，那么可以省略这个注解\n`@Ingore`忽略这个类属性，没有哪个表字段与它关联\n\n```\n@Table(name = \"T_User\")\npublic class User {\n    @Id(\"id\")\n    private int id;\n\n    private String username;\n    private String password;\n    private int age;\n\n    @Column(\"old_address\")\n    private String oldAddress;\n    @Column(\"now_address\")\n    private String nowAddress;\n\n    private int state;\n\n    @Column(\"created_at\")\n    private Timestamp createdAt;\n    @Column(\"updated_at\")\n    private Timestamp updatedAt;\n\n    @Ignore\n    private String remrk;\n```\n### 查询\n```\n@Select\n@OrderBy(\"id desc\")\nList\u003cUser\u003e queryUser(@Condition(criterion = Criterions.EQUAL, column = \"username\") String name,\n                    @Condition(criterion = Criterions.GREATER, attach = Attach.OR) int age,\n                    @Condition(column = \"id\", criterion = Criterions.IN) String[] ids,\n                    @Limit int limit, @OffSet int offset);\n```\n##### @Select\n* `columns`:默认 `select *`可以配置`columns(\"username,age\")`选择部分字段；\n* `SqlMode`:有两个选择，SqlMode.SELECTIVE 和 SqlMode.COMMON，区别是selective会检查查询条件的字段是否为null来实现动态的查询,\n即`\u003cif test=\"name != null\"\u003eusername = #{name}\u003c/if\u003e`\n\n##### @Condition\n* `criterion`：查询条件，`=`,`\u003c`,`\u003e`,`in`等，具体见`Criterions`\n* `column`：与表字段的对应，若与字段名相同可不配置\n* `attach`：连接 `and`,`or`， 默认是`and`\n* `test`：selective下的判断表达式，即`\u003cif test=\"username != null\"\u003e`里的test属性\n\n`@Limit`，`@OffSet`为分页字段。\n方法的参数不加任何注解一样会被当做查询条件，如下面两个函数效果是一样的：\n\n```\n@Select()\nList\u003cUser\u003e queryUser(Integer age);\n\n@Select()\nList\u003cUser\u003e queryUser(@Condition(criterion = Criterions.EQUAL, column = \"age\") Integer age);\n```\n\n#### 查询Model\n上面的例子在查询条件比较多时方法参数会比较多，我们可以把查询条件封装到一个类里，使用`@ModelConditions`来注解查询条件，注意被`@ModelConditions`只能有一个参数。\n\n```\n@Select\n@Page\n@ModelConditions({\n       @ModelCondition(field = \"username\", criterion = Criterions.EQUAL),\n       @ModelCondition(field = \"minAge\", column = \"age\", criterion = Criterions.GREATER),\n       @ModelCondition(field = \"maxAge\", column = \"age\", criterion = Criterions.LESS),\n       @ModelCondition(field = \"ids\", column = \"id\", criterion = Criterions.IN),\n       @ModelCondition(field = \"idArr\", column = \"id\", criterion = Criterions.IN, paramType = InType.ARRAY)\n})\nList\u003cUser\u003e queryUser5(UserSearch userSearch);\n```\n##### @ModelCondition\n* `field`:必填，查询条件中类对应的属性\n* `column`：对应的表字段\n* `paramType`：in 查询下才需要配置，数组为`array`,List为`collection`类型\n* `test`：selective下的判断表达式，即`\u003cif test=\"username != null\"\u003e`里的test属性\n\n`@Page`只能用在ModelConditions下的查询，并且方法参数的那个类应该有`offset`，`limit`这两个属性。\n\n**注：**\n\n```\n@Select(columns = \"username,age\")\nList\u003cUser\u003e queryUser(Integer age);\n\n@Select(columns = \"username,age\")\nList\u003cUser\u003e queryUser2param(Integer age, String username);\n\n\u003cselect id=\"queryUser\" resultMap=\"XXX\"\u003eselect username,age from T_User\n    \u003cwhere\u003e\n      \u003cif test=\"age != null\"\u003eAND age = #{age}\u003c/if\u003e\n    \u003c/where\u003e\n\u003c/select\u003e\n\n\u003cselect id=\"queryUser2param\" resultMap=\"XXX\"\u003eselect username,age from T_User\n    \u003cwhere\u003e\n      \u003cif test=\"age != null\"\u003eAND age = #{age}\u003c/if\u003e\n      \u003cif test=\"username != null\"\u003eAND username = #{username}\u003c/if\u003e\n    \u003c/where\u003e\n\u003c/select\u003e\n```\n两个函数生成的sql如上，`@Select`的属性`SqlMode`默认是`Selective`，所以两个都有\u003cif\u003e条件判断，但是这里第一个函数的sql，\nMybatis不支持，执行会报错，类似`no age getter in java.lang.Interger`，Mybatis会把这唯一的一个参数当做对象来取里面的值。\n解决方法：函数签名里强加`@Param()`注解，或者`@Select`里使用`sqlMode = SqlMode.COMMON`去掉生成sql里的if判断。\n这个问题只会在方法只有一个参数的情况下发生，第二个函数生成的sql是ok的。\n#### 分页\n查询参数里`@Limit`，`@OffSet`或查询model里`@Page`的分页功能都比较原始，TgDao只是一款SQL生成器而已，因此你可以使用各种插件，\n或者与其他框架集成。对于分页，可以无缝与[PageHelper](https://github.com/pagehelper/Mybatis-PageHelper)整合。\n\n```\n@Select\nList\u003cUser\u003e queryUser2(@Condition(criterion = Criterions.GREATER, column = \"age\") int min,\n                @Condition(criterion = Criterions.LESS, column = \"age\") int max);\n\n\n@Test\npublic void testQueryUser2() throws Exception {\n   PageHelper.offsetPage(1, 10);\n   List\u003cUser\u003e users = mapper.queryUser2(12, 30);\n   PageInfo page = new PageInfo\u003c\u003e(users);\n   System.out.println(page.getTotal());\n   Assert.assertTrue(page.getList().size() \u003e 0);\n}\n```\n\n---\n### 插入\n```\n@Insert(useGeneratedKeys = true, keyProperty = \"id\")//获取自增id\nint insert(User user);\n\n@BatchInsert(columns = \"username,age\")//插入的列\nint batchInsert(List\u003cUser\u003e users);\n```\n`BatchInsert`强烈建议写columns，因为生成的语句并不会过滤null字段，数据库中插入null易报错。\n\n---\n### 更新\n```\n@Update(columns = \"username,age\")//选择更新某几个列\n@ModelConditions({\n       @ModelCondition(field = \"id\")\n})\nint update(User user);\n```\n\n---\n### 删除\n```\n@Delete\nint delete(@Condition(criterion = Criterions.GREATER, column = \"age\") int min,\n          @Condition(criterion = Criterions.LESS, column = \"age\") int max);\n\n@Delete\n@ModelConditions({\n       @ModelCondition(attach = Attach.AND, field = \"minAge\", column = \"age\", criterion = Criterions.GREATER),\n       @ModelCondition(attach = Attach.AND, field = \"maxAge\", column = \"age\", criterion = Criterions.LESS)\n})\nint delete2(UserSearch userSearch);\n```\n### selective\n`@Select`，`@Count`，`@Update`，`@Delete`都有`selective`这个属性，这个属性有两个值，分别是`SqlMode.COMMON`和`SqlMode.SELECTIVE`。\n它们的区别在下面这段生成的xml里显示的很清楚，`SqlMode.SELECTIVE`引入了Mybatis的动态SQL能力。\n```\n  \u003c!-- SELECTIVE --\u003e\n  \u003cselect id=\"queryUser\" resultMap=\"BaseResultMap\"\u003eselect username,age from T_User \n    \u003cwhere\u003e\n      \u003cif test=\"name!=null and name!=''\"\u003eAND username = #{name}\u003c/if\u003e\n      \u003cif test=\"age != null\"\u003eOR age = #{age}\u003c/if\u003e\n    \u003c/where\u003e\n  \u003c/select\u003e\n  \n  \u003c!-- COMMON --\u003e\n  \u003cselect id=\"queryUser\" resultMap=\"BaseResultMap\"\u003eselect username,age from T_User \n    \u003cwhere\u003e\n      AND username = #{name} OR age = #{age}\n    \u003c/where\u003e\n  \u003c/select\u003e\n```\n\n`@Select`，`@Count`默认的selective属性是`SqlMode.SELECTIVE`，这样查询语句可以充分利用Mybatis的动态SQL能力。\n而`@Update`，`@Delete`默认是`SqlMode.COMMON`，这样做的原因是：selective模式下如果参数全是`null`会使得where语句里没有任何条件，\n最终变成全表的更新和删除，这是一个极其危险的动作。所以`@Update`，`@Delete`慎用`SqlMode.SELECTIVE`模式。\n\n### @Params\n在介绍这个注解时要先介绍一下Mybatis自己的`@Param`注解，`@Param`注解在方法的参数上，给参数定义了一个名字，\n这样可以在xml的sql里使用这个名字来取得参数所对应的值。如下：\n```\n    List\u003cUser\u003e queryUser(@Param(\"name\") String name);\n    \n    \u003cselect id=\"queryUser\"\u003eselect * from T_User where username=#{name} \u003c/select\u003e\n```\n明明参数就叫name,为什么还要`@Param`注解一个名字name呢？这是因为Java编译完，会丢掉参数名，以至于运行期mybatis不知道这个参数叫什么，所以需要注解一个名字。\n在运行时看到mybatis报错如：`Parameter 'XXX' not found. Available parameters are...` 这就是没有这个注解导致的问题。\n但是在Java8里我们已经可以通过给javac 添加`-parameters`参数来保留参数名字信息，这样mybatis会利用这个信息，这样就不需要加`@Param`注解了。\nmaven可以通过如下方式设置：\n```\n  \u003cplugin\u003e\n      \u003cgroupId\u003eorg.apache.maven.plugins\u003c/groupId\u003e\n      \u003cartifactId\u003emaven-compiler-plugin\u003c/artifactId\u003e\n      \u003cversion\u003e3.1\u003c/version\u003e\n      \u003cconfiguration\u003e\n          \u003ccompilerArgs\u003e\n              \u003carg\u003e-parameters\u003c/arg\u003e\n          \u003c/compilerArgs\u003e\n      \u003c/configuration\u003e\n  \u003c/plugin\u003e\n```\n然而有一种情况`-parameters`也无能为力，`List\u003cUser\u003e queryUser4(List ids);`当参数是collection或者数组类型时，mybatis依旧无法认出`ids`这个参数，只认`collection`和`array`。\n而`@Params`注解是Mybatis自身注解`@Param`和`-parameters`外的另外一种解决方案。`@Params`可以注解在类和方法上，\n被它注解的类和方法会在编译期自动给所有方法参数加上`@Param`注解，它借鉴了lombok的方式在编译期修改抽象语法树从而改变编译生成的字节码文件。\n```\n    @Select(columns = \"username,age\")\n    @Params\n    List\u003cUser\u003e queryUser(Integer age, String username);\n    \n    //编译后\n    List\u003cUser\u003e queryUser(@Param(\"age\") Integer var1, @Param(\"username\") String var2);\n```\n\n更多请看[example](https://github.com/twogoods/TgDao/tree/master/example)\n\n---\n## 说明\n* 编译生成的XML文件与Mapper接口在同一个包下\n* 只支持Java8和MySql\n* 修改了源代码中方法的定义或者model里和数据表的映射关系，发现编译出来的xml却没有改变，这是增量编译的原因。生成一个xml同时需要model和mapper interface两个部分，\n如果你只修改了其中一个的代码，那么另一个未修改的代码编译器就不做处理，这样这一次编译就无法得到全部的信息，所以TgDao无法生成最新版本的xml。\n解决方法是每次`mvn clean compile`先清除一下编译目录，更好的方案正在寻找...\n\n### 资料\n增量编译和`annotation processors` https://issues.gradle.org/browse/GRADLE-3259\n\nhow to debug http://blog.jensdriller.com/how-to-debug-a-java-annotation-processor-using-intellij/\n\n修改ast的helloworld：https://gist.github.com/pietrocaselani/8624554\n\n拓展lombok http://notatube.blogspot.hk/2010/12/project-lombok-creating-custom.html","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwogoods%2Ftgdao","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftwogoods%2Ftgdao","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwogoods%2Ftgdao/lists"}