在《一口一口吃掉Hibernate(一)——使用SchemaExport生成數據表》中介紹了如何生成數據表。但是這只是最基本的。Hibernate在生成或者操作數據庫時,會受一些限制。比如一開始設計表的時候,直接寫成了user(id,name,password,createTime) 這種格式的。但是客戶後來要求表名稱要以“t_”開頭,字段名要以“stu_”開頭,可以是一些數據庫的關鍵字,單詞之間以“_”隔開,方便他們維護.例如:T_User(stu_Id,stu_Name,stu_Create_Time)。
對於上面的問題,常規的方法就是去修改Hibernate_cfg.xml中的表,為每個表添加“t_”,或者寫table屬性,字段也重新使用column屬性來修改。如果你有100張表,每個表平均有10個字段,你還會去一個一個修改麼?還有可能漏掉或者改錯。最“可惡”的是,如果客戶再讓你改成其他的呢,是不是還要全部修改一次?
有沒有一種方法,不修改原先的配置,添加並配置一下表和字段的前後綴及分隔符等等,既可以完成呢??經過嘗試,終於被我拿下了,現在拿出來與大家分享。
針對上述問題,我主要用的是NamingStrategy接口。這個接口很有意思,可以作為業務類規范和數據庫表規范的橋梁,它可以表和列的映射關系的轉變和處理。例如一個數據對象User,對應數據庫表是T_USER,如果所有映射關系都是這樣的情況,可以使用NamingStrategy做為一個橋梁銜接,而不用再hbm.xml映射文件中指定class對應的物理table。
先說一下Hibernate讀取映射文件的簡要過程:循環讀取class標簽,判斷class標簽的table屬性值是否為null,若為null,則調用NamingStrategy接口的classToTableName方法將class值轉換並處理(按命名策略NamingStrategy處理)的表名。若不為null,則說明設置了table,那麼調用NamingStrategy接口的tableName方法獲取處理過的表名。循環遍歷property,判斷property標簽的column屬性值是否為null,若為null,則首先調用propertyToColumnName方法,將屬性值轉化為列名,再調用logicalColumnName方法。若不為null,則直接調用NamingStrategy接口的logicalColumnName方法,再調用columnName方法進行處理(按命名策略NamingStrategy處理)。
了解了上面說的這個過程,只要我們實現NamingStrategy這個接口,就可以隨心所欲的修改表映射了。不多說了,直接上代碼吧:
【MyNamingStrategy】自定義的命名策略,可對表、字段進行統一處理。
package com.tgb.hibernate.neatlyDB; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.NamingStrategy; import org.hibernate.util.StringHelper; /** * 數據庫命名策略,可定制表、字段等的前後綴 * * @author Longxuan * */ public class MyNamingStrategy implements NamingStrategy { /** * 數據庫命名策略單一實例 */ private static MyNamingStrategy instance = null; /** * 數據庫配置信息類 */ private static DatabaseConfiguration dbcfg = null; /** * 私有化構造方法 */ private MyNamingStrategy() { } /** * 使用單利模式,獲取數據庫命名策略的唯一實例 * * @return */ public static synchronized MyNamingStrategy getInstance() { // 默認讀取hibernate.cfg.xml文件 Configuration cfg = new Configuration().configure(); return getInstance(cfg); } /** * 使用單利模式,獲取數據庫命名策略的唯一實例,同時使用默認Hibernate.cfg.xml更新命名策略信息 * * @param cfg * 配置文件信息 * @return */ public static synchronized MyNamingStrategy getInstance(Configuration cfg) { if (instance == null) { instance = new MyNamingStrategy(); } MyNamingStrategy.dbcfg = new DatabaseConfiguration(cfg); return instance; } /** * 使用單利模式,獲取數據庫命名策略的唯一實例,同時更新命名策略信息 * * @param cfg * 配置文件信息 * @return *URL:http://www.bianceng.cn/Programming/Java/201410/45829.htm */ public static synchronized MyNamingStrategy getInstance( DatabaseConfiguration dbcfg) { if (instance == null) { instance = new MyNamingStrategy(); } MyNamingStrategy.dbcfg = dbcfg; return instance; } /** * 設置或更新數據庫配置信息 * * @param dbcfg */ public void setDBConfig(DatabaseConfiguration dbcfg) { MyNamingStrategy.dbcfg = dbcfg; } /** * 轉化為物理表名 * * @param className hbm.xml中的對應表的class值 */ @Override public String classToTableName(String className) { if(className == null ) return null; //改變大小寫 String str =UpdateStyle(className,dbcfg.getTableStyle().name()); //添加前後綴 str = addPrefixORStuffix(str, dbcfg.getTablePrefix(),dbcfg.getTableSuffix()); //添加分隔符 str = addSeparator(str,dbcfg.getTableSeparator()); return str; } @Override public String collectionTableName(String ownerEntity, String ownerEntityTable, String associatedEntity, String associatedEntityTable, String propertyName) { return null; } /** * 將讀取到的列名傳遞過來進行加工處理,變為物理表的字段名 * * @param columnName logicalColumnName方法讀取到的列名 */ @Override public String columnName(String columnName) { //先調用logicalColumnName方法,拿到處理過的值, //再傳遞到該方法中,所以可以在這兩個方法中任一選擇修改。 //return columnName; if(columnName == null ) return null; //改變大小寫 String str =UpdateStyle(columnName,dbcfg.getColumnTyle().name()); //添加前後綴 str = addPrefixORStuffix(StringHelper.unqualify(str), dbcfg.getColumnPrefix(),dbcfg.getColumnSuffix()); //添加分隔符 str = addSeparator(str,dbcfg.getColumnSeparator()); return str; } /** * 處理外鍵列名 * */ @Override public String foreignKeyColumnName(String propertyName, String propertyEntityName, String propertyTableName, String referencedColumnName) { if(referencedColumnName == null ) return null; //改變大小寫 String str =UpdateStyle(referencedColumnName,dbcfg.getColumnTyle().name()); //添加前後綴 str = addPrefixORStuffix(StringHelper.unqualify(str), dbcfg.getColumnPrefix(),dbcfg.getColumnSuffix()); //添加分隔符 str = addSeparator(str,dbcfg.getColumnSeparator()); return str; } /** * 關聯鍵的名稱 */ @Override public String joinKeyColumnName(String joinedColumn, String joinedTable) { if(joinedColumn == null ) return null; //改變大小寫 String str =UpdateStyle(joinedColumn,dbcfg.getColumnTyle().name()); //添加前後綴 str = addPrefixORStuffix(StringHelper.unqualify(str), dbcfg.getColumnPrefix(),dbcfg.getColumnSuffix()); //添加分隔符 str = addSeparator(str,dbcfg.getColumnSeparator()); return str; } @Override public String logicalCollectionColumnName(String columnName, String propertyName, String referencedColumn) { return null; } @Override public String logicalCollectionTableName(String tableName, String ownerEntityTable, String associatedEntityTable, String propertyName) { return null; } /** * 處理邏輯列名 * <b>若設置了column屬性,會直接調用調用該方法</b> * * @param columnName 列名 * @param propertyName 實體列名 */ @Override public String logicalColumnName(String columnName, String propertyName) { //name值對應propertyName,column值對應columnName //判斷columnName是否設置,若設置了,則返回column值,否則返回propertyName值 return columnName == null || columnName =="" ? propertyName:columnName; } /** * 處理邏輯列名 * <b>若未設置column屬性,則先調用該方法,再調用logicalColumnName方法</b> * * @param propertyName 實體列名 */ @Override public String propertyToColumnName(String propertyName) { if(propertyName == null ) return null; //改變大小寫 String str =UpdateStyle(propertyName,dbcfg.getColumnTyle().name()); //添加前後綴 str = addPrefixORStuffix(StringHelper.unqualify(str), dbcfg.getColumnPrefix(),dbcfg.getColumnSuffix()); //添加分隔符 str = addSeparator(str,dbcfg.getColumnSeparator()); return str; } /** * 處理表名 * <b>若設置了table屬性,才會調用該方法</b> */ @Override public String tableName(String tableName) { if(tableName == null ) return null; //改變大小寫 String str =UpdateStyle(tableName,dbcfg.getTableStyle().name()); //添加前後綴 str = addPrefixORStuffix(StringHelper.unqualify(str), dbcfg.getTablePrefix(),dbcfg.getTableSuffix()); //添加分隔符 str = addSeparator(str,dbcfg.getTableSeparator()); return str; } /** * 單詞分隔符 * * @param name * @return */ private String addSeparator(String str, String flag) { StringBuffer buf = new StringBuffer(str.substring(str.lastIndexOf(".")+1)); for (int i = 1; i < buf.length() - 1; i++) { if ('_' != buf.charAt(i - 1) && Character.isUpperCase(buf.charAt(i)) && !Character.isUpperCase(buf.charAt(i + 1))) { // buf.insert(i++, '_'); buf.insert(i++, flag); } } return buf.toString(); } /** * 添加前後綴 * * @param str * 字符串 * @param prefix * 前綴 * @param suffix * 後綴 * @return */ private String addPrefixORStuffix(String str, String prefix, String suffix) { StringBuffer buf = new StringBuffer(str.substring(str.lastIndexOf(".")+1)); buf.insert(buf.length(), suffix).insert(0, prefix); return buf.toString(); } /** * 更新樣式 * @param str * @param style * @return */ private String UpdateStyle(String str, String style) { if ("AU".equals(style)) { str = str.toUpperCase(); } else if ("AL".equals(style)) { str = str.toLowerCase(); } else if ("FU".equals(style)) { StringBuffer buf = new StringBuffer(str.substring(str.lastIndexOf(".")+1)); String upper =buf.substring(0,1).toUpperCase(); buf.delete(0, 1).insert(0, upper); str = buf.toString(); } else if ("FL".equals(style)) { StringBuffer buf = new StringBuffer(str.substring(str.lastIndexOf(".")+1)); String lower =buf.substring(0,1).toLowerCase(); buf.delete(0, 1).insert(0, lower); str = buf.toString(); } else{ } return str; } }
【DatabaseConfiguration】數據庫樣式配置,包括前後綴、分隔符、大小寫等
package com.tgb.hibernate.neatlyDB; import org.hibernate.cfg.Configuration; /** * 數據庫模式配置 * @author Longxuan * */ public final class DatabaseConfiguration { /** * 列名樣式 * AU-all upper case * AL-all lower case * FU-first upper case per word * FL-first lower case ,Other first letter upper case */ public static enum ColumnTyle { AU,AL,FU,FL; } /** * 表名稱樣式 * AU-all upper case * AL-all lower case * FU-first upper case per word * FL-first lower case ,Other first letter upper case */ public static enum TableStyle{ AU,AL,FU,FL; } /** * 列名樣式 * AU-all upper case * AL-all lower case * FU-first upper case per word * FL-first lower case ,Other first letter upper case */ private ColumnTyle columnTyle = ColumnTyle.FL; /** * 表名稱樣式 * AU-all upper case * AL-all lower case * FU-first upper case per word * FL-first lower case ,Other first letter upper case */ private TableStyle tableStyle = TableStyle.FU; /** * 表名稱前綴 */ private String tablePrefix = ""; /** * 表名稱後綴 */ private String tableSuffix = ""; /** * 表名稱分隔符 */ private String tableSeparator = ""; /** * 列名前綴 */ private String columnPrefix = ""; /** * 列名後綴 */ private String columnSuffix = ""; /** * 列名分隔符 */ private String columnSeparator = ""; public ColumnTyle getColumnTyle() { return columnTyle; } public void setColumnTyle(ColumnTyle columnTyle) { this.columnTyle = columnTyle; } public TableStyle getTableStyle() { return tableStyle; } public void setTableStyle(TableStyle tableStyle) { this.tableStyle = tableStyle; } public String getTablePrefix() { return tablePrefix; } public void setTablePrefix(String tablePrefix) { this.tablePrefix = tablePrefix; } public String getTableSuffix() { return tableSuffix; } public void setTableSuffix(String tableSuffix) { this.tableSuffix = tableSuffix; } public String getTableSeparator() { return tableSeparator; } public void setTableSeparator(String tableSeparator) { this.tableSeparator = tableSeparator; } public String getColumnPrefix() { return columnPrefix; } public void setColumnPrefix(String columnPrefix) { this.columnPrefix = columnPrefix; } public String getColumnSuffix() { return columnSuffix; } public void setColumnSuffix(String columnSuffix) { this.columnSuffix = columnSuffix; } public String getColumnSeparator() { return columnSeparator; } public void setColumnSeparator(String columnSeparator) { this.columnSeparator = columnSeparator; } /** * 初始化,讀取配置參數,默認讀取Hibernate.cfg.xml * @param cfg *URL:http://www.bianceng.cn/Programming/Java/201410/45829.htm */ public DatabaseConfiguration(){ ReadConfig(new Configuration().configure()); } /** * 初始化,讀取配置文件 * @param cfg */ public DatabaseConfiguration(Configuration cfg){ ReadConfig(cfg); } /** * 自動讀取配置信息 * @param cfg */ private void ReadConfig(Configuration cfg){ tableStyle = cfg.getProperty("tablestyle")==""?TableStyle.FU:TableStyle.valueOf(cfg.getProperty("tablestyle")); tablePrefix = cfg.getProperty("tablePrefix"); tableSuffix = cfg.getProperty("tablesuffix"); tableSeparator = cfg.getProperty("tableseparator"); columnTyle = cfg.getProperty("columntyle")==""?ColumnTyle.FL:ColumnTyle.valueOf(cfg.getProperty("columntyle")); columnPrefix = cfg.getProperty("columnPrefix"); columnSuffix = cfg.getProperty("columnsuffix"); columnSeparator = cfg.getProperty("columnseparator"); } }
【hibernate.cfg.xml】設置數據庫樣式配置信息
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory name="foo"> <!-- name: 配置的名稱 style: 類型 枚舉AU-all upper case AL-all lower case FU-first letter upper case per word FL-first letter lower case ,Other first letters upper case befix: 前綴 suffix: 後綴 separator: 分隔符 --> <property name="tablestyle">FU</property> <property name="tablePrefix" >T_</property> <property name="tablesuffix" ></property> <property name="tableseparator" >_</property> <property name="columntyle">FU</property> <property name="columnPrefix">[stu_</property> <property name="columnsuffix">]</property> <property name="columnseparator">_</property> <property name="hibernate.dialect" >org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.show_sql">true</property><!-- 設置是否顯示生成sql語句 --> <property name="hibernate.format_sql">false</property><!-- 設置是否格式化sql語句--> <mapping resource="com/bjpowernode/hibernate/User.hbm.xml" /> </session-factory> </hibernate-configuration>
【User.hbm.xml】映射文件可以比較隨意的修改或者設定了
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.bjpowernode.hibernate.User" table="Users"> <id name="id" column="pid"> <generator class="uuid" /> </id> <property name="name" column="userName"></property> <property name="password" column="pwd"></property> <property name="createTime"></property> <property name="expireTime"></property> </class> </hibernate-mapping>
最後修改一下【ExportDB】,主要是修改必須先設置NamingStrategy,在調用configure方法
package com.bjpowernode.hibernate; import org.hibernate.cfg.Configuration; import org.hibernate.tool.hbm2ddl.SchemaExport; import com.tgb.hibernate.neatlyDB.DatabaseConfiguration; import com.tgb.hibernate.neatlyDB.MyNamingStrategy; /** * 將hbm生成ddl * @author Longxuan * */ public class ExportDB { /** * @param args */ public static void main(String[] args) { //默認讀取hibernate.cfg.xml文件,並按照命名策略修改表、字段的名稱 //Configuration cfg = new Configuration().configure(); //注意,必須先設置NamingStrategy,在調用configure方法 Configuration cfg = new Configuration().setNamingStrategy(MyNamingStrategy.getInstance()).configure(); SchemaExport export = new SchemaExport(cfg); //true 在控制台打印ddl語句,true 導入ddl語句到數據庫,即可執行 export.create(true, true); } }
看一下結果圖吧:表名,字段名都已經修改過了。
上面演示的是生成數據表。在對數據庫的增刪改查時,只要同樣設置NamingStrategy即可。
為了大家更為方便的使用,已經做成jar包分享給大家了:http://pan.baidu.com/share/link?shareid=1107664883&uk=1259218556