项目中采用了jOOQ做为ORM框架,并使用jOOQ codegen生成Table,Record和PO。java
codegen使用说明请见这里。codegen的gradle配置请见这里。mysql
表结构:spring
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_name` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
gradle配置:sql
import groovy.xml.MarkupBuilder import org.jooq.util.GenerationTool import javax.xml.bind.JAXB buildscript { ext { springBootVersion = '1.5.4.RELEASE' } repositories { mavenCentral() } dependencies { classpath('mysql:mysql-connector-java:5.1.39') classpath('org.jooq:jooq-codegen:3.9.1') classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'org.springframework.boot' apply plugin: 'war' version = '1.0' sourceCompatibility = 1.8 repositories { mavenCentral() } configurations { providedRuntime } dependencies { compile('org.springframework.boot:spring-boot-starter-jooq') compile('mysql:mysql-connector-java:5.1.39') compile('com.zaxxer:HikariCP:2.6.1') compileOnly('org.jooq:jooq-codegen:3.9.1') } task jooq_code_generate(type: Task) { // Use your favourite XML builder to construct the code generation configuration file def writer = new StringWriter() def xml = new MarkupBuilder(writer) .configuration('xmlns': 'http://www.jooq.org/xsd/jooq-codegen-3.9.0.xsd') { jdbc() { driver('com.mysql.jdbc.Driver') url('jdbc:mysql://${IP}:${PORT}?characterEncoding=UTF-8&allowMultiQueries=true') user('${username}') password('${passwd}') } generator() { database() { name('org.jooq.util.mysql.MySQLDatabase') inputSchema('${schema}') includes("user") // excludes("schema_version") } generate([:]) { pojos true // daos true } target() { packageName('com.landas.temp') directory('src/main/java') encoding("UTF-8") } } } // Run the code generator doLast { GenerationTool.generate( JAXB.unmarshal(new StringReader(writer.toString()), org.jooq.util.jaxb.Configuration.class) ) } }
此时运行task,生成的po以下:数据库
public class User implements Serializable { private static final long serialVersionUID = -1585262004; private Integer id; private String userName; // omitted... }
可见codegen将数据库中的下划线自动转换为了驼峰命名。app
由于要兼容老接口,必须将userName改成user_name格式。框架
打开上面gradle配置中的xmlns定义URL,发现Generator中有strategy配置项:maven
<complexType name="Generator"> <all> <element name="name" type="string" default="org.jooq.util.DefaultGenerator" minOccurs="0" maxOccurs="1"/> <element name="strategy" type="tns:Strategy" minOccurs="0" maxOccurs="1"/> <element name="database" type="tns:Database" minOccurs="1" maxOccurs="1"/> <element name="generate" type="tns:Generate" minOccurs="0" maxOccurs="1"/> <element name="target" type="tns:Target" minOccurs="0" maxOccurs="1"/> </all> </complexType> <complexType name="Strategy"> <choice> <element name="name" type="string" minOccurs="0" maxOccurs="1" default="org.jooq.util.DefaultGeneratorStrategy"/> <element name="matchers" type="tns:Matchers" minOccurs="0" maxOccurs="1"/> </choice> </complexType>
查询jOOQ codegen文档,发现确实能够在generator中配置策略。按照文档教程,自定义了策略类AsInDatabaseStratege,添加到generator中,修改后的gradle文件以下:ide
// omitted ... generator() { database() { name('org.jooq.util.mysql.MySQLDatabase') inputSchema('${schema}') includes("user") // excludes("schema_version") } strategy() { name('com.landas.AsInDatabaseStrategy') } generate([:]) { pojos true // daos true } target() { packageName('com.landas.temp') directory('src/main/java') encoding("UTF-8") } } // omitted ...
AsInDatabaseStratege类以下,注意几个重写的方法getJavaMemberName/getJavaSetterName等:spring-boot
import org.jooq.util.DefaultGeneratorStrategy; import org.jooq.util.Definition; import java.io.Serializable; import java.util.Arrays; import java.util.List; /** * Created by Landas on 2017/10/17. */ public class AsInDatabaseStrategy extends DefaultGeneratorStrategy { /** * Override this to specifiy what identifiers in Java should look like. * This will just take the identifier as defined in the database. */ @Override public String getJavaIdentifier(Definition definition) { return definition.getOutputName(); } /** * Override these to specify what a setter in Java should look like. Setters * are used in TableRecords, UDTRecords, and POJOs. This example will name * setters "set[NAME_IN_DATABASE]" */ @Override public String getJavaSetterName(Definition definition, Mode mode) { String name = definition.getOutputName(); return "set" + name.substring(0, 1).toUpperCase() + name.substring(1); } /** * Just like setters... */ @Override public String getJavaGetterName(Definition definition, Mode mode) { String name = definition.getOutputName(); return "get" + name.substring(0, 1).toUpperCase() + name.substring(1); } /** * Override this method to define what a Java method generated from a database * Definition should look like. This is used mostly for convenience methods * when calling stored procedures and functions. This example shows how to * set a prefix to a CamelCase version of your procedure */ @Override public String getJavaMethodName(Definition definition, Mode mode) { return "call" + org.jooq.tools.StringUtils.toCamelCase(definition.getOutputName()); } /** * Override this method to define how your Java classes and Java files should * be named. This example applies no custom setting and uses CamelCase versions * instead */ @Override public String getJavaClassName(Definition definition, Mode mode) { return super.getJavaClassName(definition, mode); } /** * Override this method to re-define the package names of your generated * artefacts. */ @Override public String getJavaPackageName(Definition definition, Mode mode) { return super.getJavaPackageName(definition, mode); } /** * Override this method to define how Java members should be named. This is * used for POJOs and method arguments */ @Override public String getJavaMemberName(Definition definition, Mode mode) { return definition.getOutputName(); } /** * Override this method to define the base class for those artefacts that * allow for custom base classes */ @Override public String getJavaClassExtends(Definition definition, Mode mode) { return Object.class.getName(); } /** * Override this method to define the interfaces to be implemented by those * artefacts that allow for custom interface implementation */ @Override public List<String> getJavaClassImplements(Definition definition, Mode mode) { return Arrays.asList(Serializable.class.getName(), Cloneable.class.getName()); } /** * Override this method to define the suffix to apply to routines when * they are overloaded. * * Use this to resolve compile-time conflicts in generated source code, in * case you make heavy use of procedure overloading */ @Override public String getOverloadSuffix(Definition definition, Mode mode, String overloadIndex) { return "_OverloadIndex_" + overloadIndex; } }
注意,这个类直接放在项目中gradle任务运行时是引用不到的,会报找不到类的异常:
Execution failed for task ':jooq_code_generate'. > java.lang.ClassNotFoundException: com.landas.AsInDatabaseStrategy
为了让任务正常运行,须要在项目中新建buildSrc目录,将自定义的类放到buildSrc的src目录下,gradle能自动引用到此目录下的全部类(详情请查询gradle手册)。此时项目结构以下:
buildSrc中的gradle配置以下:
apply plugin: 'groovy' version=1.0 repositories { mavenCentral() } dependencies { compile('org.jooq:jooq-codegen:3.9.1') compile gradleApi() compile localGroovy() }
此时,从新运行 jooq_code_generate 任务,获得指望的结果:
public class User implements Serializable { private Integer id; private String user_name; // omitted ... public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } public String getUser_name() { return this.user_name; } public void setUser_name(String user_name) { this.user_name = user_name; } // omitted ... }