項目中採用了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 ... }