Hibernate Tools原始碼的使用

不淨之心發表於2013-03-14
Eclipse 中Hibernate tools 的安裝和使用 [url]http://zhoualine.iteye.com/blog/1190141[/url]

原始碼裡面有一個很好的格式化類:org.hibernate.tool.hbm2x.[color=darkblue][b]XMLPrettyPrinter[/b][/color]
呼叫
	public void formatFiles() {

formatXml( "xml" );
formatXml( "hbm.xml" );
formatXml( "cfg.xml" );

}

private void formatXml(String type) throws ExporterException {
List list = (List) files.get(type);
if(list!=null && !list.isEmpty()) {
for (Iterator iter = list.iterator(); iter.hasNext();) {
File xmlFile = (File) iter.next();
try {
XMLPrettyPrinter.prettyPrintFile(XMLPrettyPrinter.getDefaultTidy(), xmlFile, xmlFile, true);
}
catch (IOException e) {
throw new ExporterException("Could not format XML file: " + xmlFile,e);
}
}
}
}


[color=red][b]獲得原始碼的方法:[/b][/color]
[url]https://github.com/hibernate/hibernate-tools[/url], 然後建立maven工程
[color=red][size=large]得到原始碼後需要處理的問題:[/size][/color]
1.修改pom.xml的name等資訊,來適合自己建立的工程。
2.註釋掉pom.xml一些沒必要的資料庫資訊。
3.註釋幾個地方
		<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.8</version>
<!--<scope>test</scope>-->
</dependency>

4.加入sql server driver,先執行mavn
[color=darkblue]mvn install:install-file -Dfile=/mnt/D/work_documents/jar2mvn/sqljdbc-4.0.jar -DgroupId=sqljdbc -DartifactId=sqljdbc -Dversion=4.0 -Dpackaging=jar[/color]
再加入依賴:
        <dependency>
<groupId>sqljdbc</groupId>
<artifactId>sqljdbc</artifactId>
<version>4.0</version>
</dependency>

5. 加入
/mnt/D/work_documents/workspace_ide/HibernateTools/src/java/config/hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 連線資料庫資訊 -->
<property name="hibernate.bytecode.use_reflection_optimizer">false</property>
<property name="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="hibernate.connection.password">sa</property>
<property name="hibernate.connection.url">jdbc:sqlserver://192.168.0.196:1433;databaseName=dev_cpm</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</property>
<property name="hibernate.search.autoregister_listeners">false</property>
<!-- 程式碼生成資訊 info -->
<!--<property name="hibernatetool.metadatadialect">org.hibernate.cfg.reveng.dialect.SQLServer2008MetaDataDialect</property>-->
<property name="hibernatetool.metadatadialect">org.hibernate.cfg.reveng.dialect.SQLServerMetaDataDialect</property>

<!-- 自定義資訊 -->
<property name="custom.package">com</property>
<property name="custom.one2many">false</property>
<property name="custom.many2one">true</property>
<property name="custom.many2many">true</property>
<property name="custom.detectOptimisticLock">true</property>
<!-- 要遍歷的資料庫,這個是必需寫正確,否在找不到表 -->
<property name="custom.catlog">dev_cpm</property>
<property name="custom.schema">dbo</property>
<property name="custom.isAnnotation">true</property>
<property name="custom.genPojo">true</property>
<property name="custom.genDao">true</property>
<property name="custom.outputDir">/mnt/D/work_documents/workspace_ide/HibernateTools/codes</property>
</session-factory>
</hibernate-configuration>


/mnt/D/work_documents/workspace_ide/HibernateTools/src/java/com/pandy/RunHibernateTools.java
package com.pandy;

import java.io.File;
import java.util.Properties;

import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.JDBCMetaDataConfiguration;
import org.hibernate.cfg.reveng.DefaultReverseEngineeringStrategy;
import org.hibernate.cfg.reveng.ReverseEngineeringSettings;
import org.hibernate.tool.hbm2x.DAOExporter;
import org.hibernate.tool.hbm2x.DAONewExporter;
import org.hibernate.tool.hbm2x.POJOExporter;

public class RunHibernateTools {

public void run() {

String path = getClass().getResource("/").toString();
if (path.startsWith("file:"))
path = path.substring("file:".length() + 1);

//String fileName = path + "config/hibernate.cfg.xml";
String fileName = "/mnt/D/work_documents/workspace_ide/HibernateTools/src/java/config/hibernate.cfg.xml";
File file = new File(fileName);
if (!file.exists()) {
throw new RuntimeException("找不到配置檔案");
}
// 直接給絕對路徑會出問題
Configuration xmlcfg = new Configuration().configure(file);

JDBCMetaDataConfiguration cfg = new JDBCMetaDataConfiguration();
Properties properties = xmlcfg.getProperties();
cfg.setProperties(properties);

DefaultReverseEngineeringStrategy configurableNamingStrategy = new DefaultReverseEngineeringStrategy();
configurableNamingStrategy.setSettings(new ReverseEngineeringSettings(configurableNamingStrategy)
.setDefaultPackageName(getString(properties, "custom.package", "com"))// 要生成的包名
.setCreateCollectionForForeignKey(getBoolean(properties, "custom.one2many", false))// 是否生成many-to-one的在one端的集合類,
// 就是一對多的關係
.setCreateManyToOneForForeignKey(getBoolean(properties, "custom.many2one", true))// 是否生成many-to-one
.setDetectManyToMany(getBoolean(properties, "custom.many2many", true))// 是否生成many-to-many
.setDetectOptimisticLock(getBoolean(properties, "custom.detectOptimisticLock", true)) // 樂觀鎖物件?
);
cfg.setReverseEngineeringStrategy(configurableNamingStrategy);
//cfg.readFromJDBC();// 不區分schema和catlog的話,容易碰到錯誤.
cfg.readFromJDBC(getString(properties, "custom.catlog", "Test"), getString(properties, "custom.schema", "dbo"));// 只從資料的這些資訊生成
cfg.buildMappings();

if (getBoolean(properties, "custom.genPojo", true)) {
POJOExporter exporter = new POJOExporter(cfg, getOutputDir(properties));
if (getBoolean(properties, "custom.isAnnotation", true)) {
exporter.getProperties().setProperty("ejb3", "true");// ejb3註解
exporter.getProperties().setProperty("jdk5", "true");// jdk5語法(主要是集合類的泛型處理)
}
exporter.start();
}

if (getBoolean(properties, "custom.genDao", false)) {
DAOExporter daoExporter = new DAOExporter(cfg, getOutputDir(properties));
daoExporter.start();
}
}

private File getOutputDir(Properties properties) {
File file = new File(getString(properties, "custom.outputDir", "/"));// 生成專案的物理位置(跟目錄,tools會自動根據pacakge建立相應路徑)
return file;
}

private static boolean getBoolean(Properties properties, String key, boolean defaultValue) {
String val = properties.getProperty(key);
if (isBlank(val)) {
return defaultValue;
}
return Boolean.valueOf(val);
}

private static String getString(Properties properties, String key, String defaultValue) {
String val = properties.getProperty(key);
if (isBlank(val)) {
return defaultValue;
}
return val;
}

private static Integer getInteger(Properties properties, String key, Integer defaultValue) {
String val = properties.getProperty(key);
if (isBlank(val)) {
return defaultValue;
}
return Integer.parseInt(val);
}

private static boolean isBlank(String val) {
if (val == null || val.trim().length() == 0)
return true;
return false;
}

public static void main(String[] args) {
System.out.println("----------------程式碼生成開始-----------------------");
RunHibernateTools tools = new RunHibernateTools();
tools.run();
System.out.println("----------------程式碼生成完成-----------------------");

}

}

可能要適當修改
6.增加一個存放程式碼的資料夾
/mnt/D/work_documents/workspace_ide/HibernateTools/codes
7. /mnt/D/work_documents/workspace_ide/HibernateTools/src/java/org/hibernate/cfg/JDBCMetaDataConfiguration.java 增加一個方法:
public void readFromJDBC(String catalog, String schema) {

JDBCBinder binder = new JDBCBinder(this, buildSettings(), createMappings(), revEngStrategy);

binder.readFromDatabase(catalog, schema, buildMapping(this));

}

mian方法修改:
//cfg.readFromJDBC();// 不區分schema和catlog的話,容易碰到錯誤.
cfg.readFromJDBC(getString(properties, "custom.catlog", "Test"), getString(properties, "custom.schema", "dbo"));// 只從資料的這些資訊生成

8.生成的DAO字尾是Home,要是改成XxxxDAO,以DAO為字尾。
/mnt/D/work_documents/workspace_ide/HibernateTools/src/java/org/hibernate/tool/hbm2x/DAOExporter.java, 修改以下方法為:
protected void init() {
super.init();
setTemplateName(DAO_DAOHOME_FTL);
setFilePattern("{package-name}/{class-name}DAO.java");
}

9.模板檔案,如果生成的程式碼需要修改,可以修改模板,路徑為
/mnt/D/work_documents/workspace_ide/HibernateTools/src/templates
10. [color=red]配置需要生成程式碼的表[/color]
A: 跟蹤程式碼
/mnt/D/work_documents/workspace_ide/HibernateTools/src/java/org/hibernate/tool/hbm2x/GenericExporter.java
[color=red]前面有一段static程式碼[/color],裡面有 ge.getConfiguration().getClassMappings(),ge.getConfiguration()裡面有一個tables屬性,對映了table=>java file.
B:到/mnt/D/work_documents/workspace_ide/HibernateTools/src/java/org/hibernate/tool/hbm2x/AbstractExporter.java 去檢視這個configuration到底怎麼初始化?
可以看到它的初始化是GenericExporter建構函式裡面傳入。那麼就要找到什麼時候建立GenericExporter?
C:
它是在POJOExporter建構函式裡面,那麼就在main函式的POJOExporter exporter = new POJOExporter(cfg, getOutputDir(properties));建立。
D:到main函式更在cfg的建立,並監視裡面的tables什麼時候有值?cfg.readFromJDBC()這裡被賦值,繼續跟蹤。
D: 跟蹤到/mnt/D/work_documents/workspace_ide/HibernateTools/src/java/org/hibernate/cfg/JDBCBinder.java,readFromDatabase()方法DatabaseCollector collector = readDatabaseSchema(catalog, schema),被賦值。
E: 讓它繼續找,我們只是在生成的時候過濾掉就可以了。[color=red]那麼回到A,在這裡做手腳[/color]。
-----------
I: /mnt/D/work_documents/workspace_ide/HibernateTools/src/java/config/hibernate.cfg.xml增加一個
<!--  需要都好隔開表名  -->
<property name="custom.filter.include">app_user,rh_log_exception_info,rh_log_exception_param</property>

II:/mnt/D/work_documents/workspace_ide/HibernateTools/src/java/org/hibernate/cfg/JDBCMetaDataConfiguration.java最後面增加
public String[] filterIncludeTables = null;
public void setFilterIncludeTables(String[] filters) {
this.filterIncludeTables = filters;
for (int i = 0; i < filters.length; i++) {
this.filterIncludeTables[i] = filters[i].replace("_", "");
}
}

public boolean isInclude(String shortName) {
if(filterIncludeTables==null) return true;

for (int i = 0; i < filterIncludeTables.length; i++) {
if(shortName.equalsIgnoreCase(filterIncludeTables[i])) return true;
}
return false;
}

III: /mnt/D/work_documents/workspace_ide/HibernateTools/src/java/org/hibernate/tool/hbm2x/GenericExporter.java修改static段的程式碼:
	static {
modelIterators.put( "configuration", new ModelIterator() {

void process(GenericExporter ge) {
TemplateProducer producer = new TemplateProducer(ge.getTemplateHelper(),ge.getArtifactCollector());
producer.produce(new HashMap(), ge.getTemplateName(), new File(ge.getOutputDirectory(),ge.filePattern), ge.templateName, "Configuration");

}

});
modelIterators.put("entity", new ModelIterator() {

void process(GenericExporter ge) {
Iterator iterator = ge.getCfg2JavaTool().getPOJOIterator(ge.getConfiguration().getClassMappings());
Map additionalContext = new HashMap();
while ( iterator.hasNext() ) {
POJOClass element = (POJOClass) iterator.next();
JDBCMetaDataConfiguration cfg = (JDBCMetaDataConfiguration)ge.getConfiguration();
if(cfg.isInclude(element.getShortName())){
ge.exportPersistentClass( additionalContext, element );
}
}
}
});
modelIterators.put("component", new ModelIterator() {

void process(GenericExporter ge) {
Map components = new HashMap();

Iterator iterator = ge.getCfg2JavaTool().getPOJOIterator(ge.getConfiguration().getClassMappings());
Map additionalContext = new HashMap();
while ( iterator.hasNext() ) {
POJOClass element = (POJOClass) iterator.next();
ConfigurationNavigator.collectComponents(components, element);
}

iterator = components.values().iterator();
while ( iterator.hasNext() ) {
Component component = (Component) iterator.next();
ComponentPOJOClass element = new ComponentPOJOClass(component,ge.getCfg2JavaTool());
JDBCMetaDataConfiguration cfg = (JDBCMetaDataConfiguration)ge.getConfiguration();
if(cfg.isInclude(element.getShortName())){
ge.exportComponent( additionalContext, element );
}
}
}
});
}

這樣就搞定了。可以模仿做排除功能。
11.打包成jar, 參考:[url]http://panyongzheng.iteye.com/blog/1759912[/url],[url]http://sundful.iteye.com/blog/1850070[/url],修改pom.xml使用maven-compiler-plugin外掛的java版本為1.7。 並修改外掛的版本號[url]http://qiang106.iteye.com/blog/1388645[/url],
			<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<includes>
<include>**/*.java</include>
</includes>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
......
......
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>./src/assembly/dist.xml</descriptor>
</descriptors>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>

命令為:
測試並打包:[color=darkblue]mvn -e clean package[/color]
不需要測試打包:[color=darkblue]mvn -e clean package -DskipTests[/color]
複製原始碼,不測試打包:[color=darkblue]mvn clean assembly:assembly -DskipTests[/color]
12.執行jar:
複製得到依賴jar: [color=darkblue]mvn dependency:copy-dependencies -DoutputDirectory=target/lib[/color]
執行不含原始碼jar:[color=darkblue]java -jar /mnt/D/work_documents/workspace_ide/HibernateTools/target/hibernate-tools-Pandy.4.0.0-CR1.jar[/color]
執行含原始碼的jar:[color=darkblue]java -jar /mnt/D/work_documents/workspace_ide/HibernateTools/target/hibernate-tools-Pandy.4.0.0-CR1-jar-with-dependencies.jar[/color]
最後的[color=red]打包並執行[/color]的兩個命令:
[color=red]mvn -e clean package dependency:copy-dependencies -DskipTests -DoutputDirectory=target/lib[/color]
[color=red]java -jar /mnt/D/work_documents/workspace_ide/HibernateTools/target/hibernate-tools-Pandy.4.0.0-CR1.jar[/color]

13.執行是輸入hibernate.cfg.xml檔案路徑
A:修改run方法,增加一個hibernate.cfg.xml的絕對路徑引數,並註釋掉原本硬編碼路徑:

B:修改mian方法:
public static boolean validate(String[] args){
if(args==null||args.length==0) {
System.out.println("ERROR: 一定要輸入一個引數,這個引數是hibernate.cfg.xml的絕對路徑.");
return false;
}
String filePath = args[0];

File file = new File(filePath);
if(!file.exists()){
System.out.println("ERROR: hibernate.cfg.xml檔案不存在.");
return false;
}
return true;
}

public static void main(String[] args) {
System.out.println("驗證引數:");
if(!validate(args)){
System.exit(0);
}

System.out.println("----------------程式碼生成開始-----------------------");
RunHibernateTools tools = new RunHibernateTools();
tools.run(args[0]);
System.out.println("----------------程式碼生成完成-----------------------");

}

最終執行命令: [color=red]java -jar /mnt/D/work_documents/workspace_ide/HibernateTools/target/hibernate-tools-Pandy.4.0.0-CR1.jar /mnt/D/work_documents/workspace_ide/HibernateTools/hibernate.cfg.xml[/color]
14.POJO的原始碼主鍵生成
org.hibernate.tool.hbm2x.pojo.EntityPOJOClass, generateAnnIdGenerator()方法。因為不知道怎麼傳入主鍵的配置,所以嘗試在這裡修改。我改成了:要麼是uuid,要麼是auto型別,這個是自己的需求。
			if ( !"native".equals( strategy ) ) {
if ( "guid".equals( strategy )||"uuid".equals( strategy ) ) {
//增加uuid的註解
builder.resetAnnotation( importType("javax.persistence.GeneratedValue") );
builder.addQuotedAttribute( "generator", "system-uuid" );
idResult.append(builder.getResult());
//增加一個註解
AnnotationBuilder builder1=AnnotationBuilder.createAnnotation( importType("org.hibernate.annotations.GenericGenerator") )
.addQuotedAttribute( "name", "system-uuid" )
.addQuotedAttribute( "strategy", "org.hibernate.id.UUIDGenerator");
idResult.append(builder1.getResult());
}
else {
/*builder.resetAnnotation( importType("javax.persistence.GeneratedValue") );
builder.addAttribute( "strategy", staticImport("javax.persistence.GenerationType", "IDENTITY" ) );
idResult.append(builder.getResult());*/
builder.resetAnnotation( importType("javax.persistence.GeneratedValue") );
//使用自動主鍵生成器
builder.addAttribute( "strategy", staticImport("javax.persistence.GenerationType", "GenerationType.AUTO" ) );
idResult.append(builder.getResult());
}
}else {
builder.resetAnnotation( importType("javax.persistence.GeneratedValue") );
idResult.append(builder.getResult());
}



[b]修改原始碼預設產生equal,toString,hashCode方法:[/b]
/mnt/D/work_documents/workspace_ide_linux/HibernateTools/src/java/org/hibernate/tool/hbm2x/pojo/BasicPOJOClass.java
[b]toString:[/b]
private boolean needsToString(Iterator iter) {
/*while ( iter.hasNext() ) {
Property element = (Property) iter.next();
if ( c2j.getMetaAsBool( element, "use-in-tostring" ) ) {
return true;
}
}
return false;*/
return true;
}

private Iterator getToStringPropertiesIterator(Iterator iter) {
List properties = new ArrayList();

while ( iter.hasNext() ) {
Property element = (Property) iter.next();
//if ( c2j.getMetaAsBool( element, "use-in-tostring" ) ) {
properties.add( element );
//}
}

return properties.iterator();
}

[b]equal & hashCode:[/b]
private boolean needsEqualsHashCode(Iterator iter) {
while ( iter.hasNext() ) {
Property element = (Property) iter.next();
//if ( usePropertyInEquals( element ) ) {
return true;
//}
}
return false;
}

public String generateHashCode(Property property, String result, String thisName, boolean jdk5) {
StringBuffer buf = new StringBuffer();
//if ( c2j.getMetaAsBool( property, "use-in-equals" ) ) {
...... 裡面的不用註釋
//}
return buf.toString();
}



[b]修改版本號資訊:[/b]
/mnt/D/work_documents/workspace_ide_linux/HibernateTools/src/java/org/hibernate/tool/Version.java


自己的修改hibernate-tools的模板和修復Comment亂碼問題:[url]http://kennylee26.iteye.com/blog/1229993[/url]
Hibernate Tools生成註釋:[url]http://www.blogjava.net/pauliz/archive/2009/11/13/302162.html[/url]
修改原始碼的地方:
[color=red][b]輸出中文亂碼[/b][/color]:
1.
TemplateProducer.produce(), 應該使用UTF-8輸出到檔案.
Writer fileWriter = null;
....
fileWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(destination), "UTF-8"));
2.src/org/hibernate/tool/hbm2x/jtidy.properties
input-encoding=utf-8
output-encoding=utf-8
3.反轉表名等類,都存放在:org.hibernate.cfg.reveng.dialect, 並針對相應類過載getTables方法.
[b][color=red]DAO:[/color][/b]
src/org/hibernate/tool/hbm2x/DAOExporter.java, 這裡修改輸出類的檔名,extends,implements等.
dao/daohome.ftl, 這裡可以修改預設的方法,和類名字等.
[b][color=red]POJO:[/color][/b]
src/org/hibernate/tool/hbm2x/POJOExporter.java, 這裡修改輸出類的extends,implements等.
pojo/Pojo.ftl

去掉註解的schema,catlog, Ejb3TypeDeclaration.ftl,Line8,7

POJO的頭部註釋資訊:生成表的註釋:
A:修改或者過載JDBCMetaDataDialect的繼續類的getTables()方法,確保這裡的rs.getString("remarks")能得到資料庫的註釋,否則自己查詢資料庫得到註釋.
B:src/pojo/PojoTypeDeclaration.ftl,增加表註釋
${pojo.getClassJavaDoc(pojo.getDeclarationName() + " By Pandy.", 0)}
改為
<#if clazz.table.comment?exists>
/**
* Entity: ${clazz.table.comment}
*/
</#if>

extends:
BasicPOJOClass.getExtendsDeclaration(), Line190
EntityPOJOClass.getExtends(), 寫入extends, 或者直接Line84固定返回extends

implements:
BasicPOJOClass.getImplementsDeclaration(), Line200
EntityPOJOClass.getImplements(), Line87
給POJO增加一些預設的函式:
src/pojo/Pojo.ftl, Line11,
增加serialVersionUID:
src/pojo/Pojo.ftl, Line7
private static final long serialVersionUID = 1L;


[color=red][b]修改資料庫語言:Dialect[/b][/color], 這裡影響到pojo.id的註解
MetaDataDialectFactory.createMetaDataDialect();
這裡沒有正確從檔案hibernate.properties獲得dialect, 所以直接修改property=預設的dialect,
比如:String property ="org.hibernate.cfg.reveng.dialect.SQLServerMetaDataDialect";
SQLServerMetaDataDialect這個是在Hibernate-Tools.jar裡面的.


[url]http://tonydark01.iteye.com/blog/1721834[/url]
直接用junit或mian函式執行,而非依賴於jboss的外掛,省的更新eclipse或者遷移的時候各種麻煩 ,副專案包備份.
1.增加slf4j-simple-1.5.2.jar,
2.增加驅動程式包,
3.給JDBCMetaDataConfiguration.java增加一個方法
public void readFromJDBC(String catalog, String schema) {
JDBCBinder binder = new JDBCBinder(this, buildSettings(), createMappings(), revEngStrategy);
binder.readFromDatabase(catalog, schema, buildMapping(this));
}

4.給hibernate.cfg.xml增加一個屬性
<property name="hibernatetool.metadatadialect">org.hibernate.cfg.reveng.dialect.SQLServer2008MetaDataDialect</property>
,
[color=red]注意,貌似許多屬性都是在這裡增加的.[/color]

相關文章