Hibernate Shards 数据的水平、垂直切割(二)- Hibernate Shards基本演示
生活随笔
收集整理的這篇文章主要介紹了
Hibernate Shards 数据的水平、垂直切割(二)- Hibernate Shards基本演示
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
準備
1. 以Hibernate Shards 數據的水平、垂直切割(一)- Hibernate測試環境的項目為基礎
2. Hibernate Shards使用了commons logging,下載個項目最新的release版本。這里用的版本為1.1.1
3. 在Hibernate Shards 數據的水平、垂直切割(一)- Hibernate測試環境中,我們在mysql中建立了一個hbshards數據庫,為了測試shards再建立一個hbshards2的數據庫,contact表的結構一樣
4. 將Hibernate Shards發布包中的hibernate-shards.jar放到lib目錄
????項目引用添加hibernate-shards.jar
????將commons logging發布包中的commons-logging-1.1.1.jar放到lib目錄
????CLASSPATH中添加hibernate-shards.jar、commons-logging-1.1.1.jar
Hibernate Shards測試項目
出于結構上的簡單考慮,定義一個ShardableEntity的接口,讓ContactEntity實現這個接口:
public interface ShardableEntity {public String getIdentifier(); } public class ContactEntity implements ShardableEntity {public String getIdentifier(){return this._id;}//other code omitted is the same with previous post } 映射文件ContactEntity.hbm.xml與上一篇文章中的一樣
因為我們需要將contact的數據以切片的形式存入2個數據庫中,因此需要配置這2個數據庫的信息。Hibernate Shards通過定義2個hibernate的配置文件解決這個問題:
shard0.hibernate.cfg.xml: <?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory><property name="connection.driver_class">com.mysql.jdbc.Driver</property><property name="connection.url">jdbc:mysql://localhost/hbshards</property><property name="connection.username">root</property><property name="connection.password">dev</property><property name="connection.pool_size">10</property><property name="show_sql">true</property><property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property><property name="hbm2ddl.auto">validate</property><property name="hibernate.connection.shard_id">0</property><property name="hibernate.shard.enable_cross_shard_relationship_checks">false</property><mapping resource="ContactEntity.hbm.xml" /> </session-factory> </hibernate-configuration> shard1.hibernate.cfg.xml: <?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory><property name="connection.driver_class">com.mysql.jdbc.Driver</property><property name="connection.url">jdbc:mysql://localhost/hbshards2</property><property name="connection.username">root</property><property name="connection.password">dev</property><property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property><property name="hibernate.connection.shard_id">1</property><property name="hibernate.shard.enable_cross_shard_relationship_checks">false</property> </session-factory> </hibernate-configuration> 其中shard0.hibernate.cfg.xml作為主要的配置文件,針對每個shard創建的SessionFactory對象,除了數據庫連接信息的配置之外,都來自shard0.hibernate.cfg.xml
針對其他每個shard創建的SessionFactory,數據庫連接信息從相應的配置文件(例如shard1.hibernate.cfg.xml)中讀取,讀取的內容包括connection.url、connection.username、connection.password、connection.datasource。為了hibernate加載其他shard的配置文件時不拋異常,在其他shard的配置文件中我們還是把必要的配置屬性寫上去了
hibernate.connection.shard_id屬性為每個shard定義一個id,必須有一個shard_id為0的shard,其他的shard_id可以定義為任意整數
我們假定contact的id都是由數字組成的一個8位長度的字符串
對contact的切割策略簡單定義為: contact id第一個字符為0-4的存入shard0中,為5-9的存入shard1中
為了實現這一策略,我們需要實現hibernate shards的2個接口
ShardResolutionStrategy接口的實現: import java.util.List; import java.util.ArrayList; import org.hibernate.shards.strategy.resolution.ShardResolutionStrategy; import org.hibernate.shards.strategy.selection.ShardResolutionStrategyData; import org.hibernate.shards.ShardId; /** a simple ShardResolutionStrategy implementation for our ContactEntity*/ public class MyShardResolutionStrategy implements ShardResolutionStrategy {private List<ShardId> _shardIds;public MyShardResolutionStrategy(List<ShardId> shardIds){this._shardIds = shardIds;}public List selectShardIdsFromShardResolutionStrategyData(ShardResolutionStrategyData arg0){List ids = new ArrayList();String id = (String)arg0.getId();if(id==null || id.isEmpty()) ids.add(this._shardIds.get(0));else{//our shard selection is identified by the //first char(number) in contact id//0-4 => shards0, 5-9 => shards1Integer i = new Integer(id.substring(0, 1));ids.add(this._shardIds.get(i/5));}return ids;} } ShardSelectionStrategy接口的實現: import java.util.List; import org.hibernate.shards.ShardId; import org.hibernate.shards.strategy.selection.ShardSelectionStrategy; /** a simple ShardSelectionStrategy implementation for our ContactEntity*/ public class MyShardSelectionStrategy implements ShardSelectionStrategy {private List<ShardId> _shardIds;public MyShardSelectionStrategy(List<ShardId> shardIds){this._shardIds=shardIds;}public ShardId selectShardIdForNewObject(Object obj) {if(obj instanceof ShardableEntity) {String id = ((ShardableEntity)obj).getIdentifier();if(id==null || id.isEmpty()) return this._shardIds.get(0);Integer i = new Integer(id.substring(0, 1));//our shard selection is identified by the //first char(number) in contact id//0-4 => shards0, 5-9 => shards1return this._shardIds.get(i/5);}//for non-shardable entities we just use shard0return this._shardIds.get(0);} } 接下來就是怎么使用hibernate shards的測試代碼了: import java.util.Iterator; import java.util.List; import java.util.ArrayList; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.shards.*; import org.hibernate.shards.cfg.*; import org.hibernate.shards.strategy.*; import org.hibernate.shards.strategy.access.*; import org.hibernate.shards.strategy.resolution.*; import org.hibernate.shards.strategy.selection.*;
public class Main {public static void main(String[] args) {HibernateShardsTest(args);}private static SessionFactory createSessionFactory() {//加載主配置文件,為每個shard創建SessionFactory對象時將//以他作為原型Configuration prototypeCfg = new Configuration().configure("shard0.hibernate.cfg.xml");//每個shard的配置文件List<ShardConfiguration> shardCfgs = new ArrayList<ShardConfiguration>();shardCfgs.add(buildShardConfig("shard0.hibernate.cfg.xml"));shardCfgs.add(buildShardConfig("shard1.hibernate.cfg.xml"));//數據切片策略的工廠對象ShardStrategyFactory strategyFactory = buildShardStrategyFactory();ShardedConfiguration shardedConfig = new ShardedConfiguration(prototypeCfg, shardCfgs, strategyFactory);//返回一個ShardedSessionFactory對象return shardedConfig.buildShardedSessionFactory();}private static ShardStrategyFactory buildShardStrategyFactory() {ShardStrategyFactory factory = new ShardStrategyFactory() {//測試用的自定義數據切片策略的工廠類public ShardStrategy newShardStrategy(List<ShardId> shardIds) {ShardSelectionStrategy ss = new MyShardSelectionStrategy(shardIds);ShardResolutionStrategy rs = new MyShardResolutionStrategy(shardIds);ShardAccessStrategy as = new SequentialShardAccessStrategy();return new ShardStrategyImpl(ss, rs, as);}};return factory;}private static ShardConfiguration buildShardConfig(String configFile) {Configuration config = new Configuration().configure(configFile);return new ConfigurationToShardConfigurationAdapter(config);}private static void HibernateShardsTest(String[] args){String loginId = "RicCC@cnblogs.com";String password = "123";if(args!=null && args.length==2){loginId = args[0];password = args[1];}SessionFactory factory = null;try{factory = createSessionFactory();ShardsTestCreate(factory);ShardsTestLogin(factory, loginId, password);ShardsTestDelete(factory);}catch(Exception e){System.out.println(e.getMessage());e.printStackTrace();}finally{if(factory!=null) factory.close();}}private static void ShardsTestCreate(SessionFactory factory){Session session = null;Transaction transaction = null;System.out.println("===Create Contacts===");try{session = factory.openSession();transaction = session.beginTransaction(); session.save(new ContactEntity("01111111","RicCC@cnblogs.com", "123", "Richie", "RicCC@cnblogs.com"));session.save(new ContactEntity("91111111","a@cnblogs.com", "123", "AAA", "a@cnblogs.com"));session.save(new ContactEntity("81111111","b@cnblogs.com", "123", "BBB", "b@cnblogs.com"));session.save(new ContactEntity("31111111","c@cnblogs.com", "123", "CCC", "c@cnblogs.com"));transaction.commit();}catch(Exception e){if(transaction!=null) transaction.rollback();System.out.println(e.getMessage());e.printStackTrace();}finally{if(session!=null) session.close();}}private static void ShardsTestLogin(SessionFactory factory, String loginId, String password){Session session = null;ContactEntity c = null;System.out.println("\n===Login Test===");try{session = factory.openSession();List contacts = session.createQuery("from ContactEntity where LoginId=:loginId").setString("loginId", loginId).list();if(contacts.isEmpty())System.out.println("Contact \"" + loginId + "\" not found!");else{c = (ContactEntity)contacts.get(0);if(c.getPassword().equals(password))System.out.println("Contact \"" + loginId + "\" login successful");else System.out.println("Password is incorrect (should be: "+ c.getPassword() + ", but is: " + password + ")");}System.out.println("\n===Get Contact by Id===");c = (ContactEntity)session.get(ContactEntity.class, "81111111");System.out.println(c.toString());c = (ContactEntity)session.get(ContactEntity.class, "31111111");System.out.println(c.toString());}catch(Exception e){System.out.println(e.getMessage());e.printStackTrace();}finally{if(session!=null) session.close();}}private static void ShardsTestDelete(SessionFactory factory){Session session = null;Transaction transaction = null;System.out.println("\n===Delete Contacts===");try{session = factory.openSession();transaction = session.beginTransaction();List contacts = session.createQuery("from ContactEntity").list();Iterator it = contacts.iterator();while(it.hasNext()){session.delete(it.next());}transaction.commit();}catch(Exception e){if(transaction!=null) transaction.rollback();System.out.println(e.getMessage());e.printStackTrace();}finally{if(session!=null) session.close();}} } 項目結構圖:
?? ?
運行測試
用Eclipse編譯class文件,將生成的所有class文件以及相關的配置、映射文件 拷貝到lib目錄中,運行Main.class結果如下:
D:\Work\research\Java\Hibernate-Test\lib>java Main
===Create Contacts===
Hibernate: insert into CONTACT (EMAIL, NAME, LOGIN_ID, PASSWORD, ID) values (?,?, ?, ?, ?)
Hibernate: insert into CONTACT (EMAIL, NAME, LOGIN_ID, PASSWORD, ID) values (?,?, ?, ?, ?)
Hibernate: insert into CONTACT (EMAIL, NAME, LOGIN_ID, PASSWORD, ID) values (?,?, ?, ?, ?)
Hibernate: insert into CONTACT (EMAIL, NAME, LOGIN_ID, PASSWORD, ID) values (?,?, ?, ?, ?)
===Login Test===
Hibernate: select contactent0_.ID as ID0_, contactent0_.EMAIL as EMAIL0_, contactent0_.NAME as NAME0_, contactent0_.LOGIN_ID as LOGIN4_0_, contactent0_.PASSWORD as PASSWORD0_ from CONTACT contactent0_ where contactent0_.LOGIN_ID=?
Hibernate: select contactent0_.ID as ID0_, contactent0_.EMAIL as EMAIL0_, contactent0_.NAME as NAME0_, contactent0_.LOGIN_ID as LOGIN4_0_, contactent0_.PASSWORD as PASSWORD0_ from CONTACT contactent0_ where contactent0_.LOGIN_ID=?
Contact "RicCC@cnblogs.com" login successful
===Get Contact by Id===
Hibernate: select contactent0_.ID as ID0_0_, contactent0_.EMAIL as EMAIL0_0_, contactent0_.NAME as NAME0_0_, contactent0_.LOGIN_ID as LOGIN4_0_0_, contactent0_.PASSWORD as PASSWORD0_0_ from CONTACT contactent0_ where contactent0_.ID=?
{ Id="81111111", LoginId="b@cnblogs.com", Name="BBB", EMail="b@cnblogs.com" }
Hibernate: select contactent0_.ID as ID0_0_, contactent0_.EMAIL as EMAIL0_0_, contactent0_.NAME as NAME0_0_, contactent0_.LOGIN_ID as LOGIN4_0_0_, contactent0_.PASSWORD as PASSWORD0_0_ from CONTACT contactent0_ where contactent0_.ID=?
{ Id="31111111", LoginId="c@cnblogs.com", Name="CCC", EMail="c@cnblogs.com" }
===Delete Contacts===
Hibernate: select contactent0_.ID as ID0_, contactent0_.EMAIL as EMAIL0_, contactent0_.NAME as NAME0_, contactent0_.LOGIN_ID as LOGIN4_0_, contactent0_.PASSWORD as PASSWORD0_ from CONTACT contactent0_
Hibernate: select contactent0_.ID as ID0_, contactent0_.EMAIL as EMAIL0_, contactent0_.NAME as NAME0_, contactent0_.LOGIN_ID as LOGIN4_0_, contactent0_.PASSWORD as PASSWORD0_ from CONTACT contactent0_
Hibernate: delete from CONTACT where ID=?
Hibernate: delete from CONTACT where ID=?
Hibernate: delete from CONTACT where ID=?
Hibernate: delete from CONTACT where ID=?
1. 通過id加載實體的時候,hibernate shards使用ShardSelectionStrategy來決定應當從哪個shard加載數據
2. 新增數據的時候,hibernate shards使用ShardResolutionStrategy來決定應當將數據insert到哪個shard中
?? ?這里為什么不能使用ShardSelectionStrategy呢?因為與id的生成機制相關,比如說可以讓數據庫來生成id(hibernate中的native方式),例如自增id,在shard的應用場景下可以簡單的給每個shard一個起止范圍,只是在insert數據的時候必須通過特定的算法決定將數據insert到哪個shard中,這樣的情況下ShardSelectionStrategy是無法運用的。而對于assigned等類似的id生成機制,還是可以運用ShardSelectionStrategy策略的
3. 執行hql或者Criteria查詢的時候,則使用ShardAccessStrategy從shard中查詢數據
?? ?上面示例中的buildShardStrategyFactory方法中,我們使用了hibernate shards項目提供的SequentialShardAccessStrategy策略,這個策略在所有shards中逐個執行查詢語句,然后對各個結果進行合并
基于上面幾點,我們可以對上面測試運行后的輸出做出解釋了:
兩個使用了hql的地方,都產生了兩次sql查詢,這是因為使用了SequentialShardAccessStrategy緣故,他從我們定義的兩個shard中分別執行sql查詢,合并結果集返回給調用者
把測試代碼中的刪除語句注釋掉,運行測試后查詢數據庫,可以確認數據都是正確的insert在相應的shard中的
總結
上面演示了hibernate shards的基本用法,總體來看使用上非常簡單,只需要針對實施shard的實體使用hibernate shards提供的ShardedSessionFactory即可
使用hibernate shards也存在一些限制,但大部分都是使用shards后的一些設計要求,或者是目前的it技術背景下對shards設計上的約束,在后面一篇中再詳細整理一下這方面內容
文中使用的測試項目源文件在這里: 下載。引用到的jar文件需要從相關項目網站去下載
1. 以Hibernate Shards 數據的水平、垂直切割(一)- Hibernate測試環境的項目為基礎
2. Hibernate Shards使用了commons logging,下載個項目最新的release版本。這里用的版本為1.1.1
3. 在Hibernate Shards 數據的水平、垂直切割(一)- Hibernate測試環境中,我們在mysql中建立了一個hbshards數據庫,為了測試shards再建立一個hbshards2的數據庫,contact表的結構一樣
4. 將Hibernate Shards發布包中的hibernate-shards.jar放到lib目錄
????項目引用添加hibernate-shards.jar
????將commons logging發布包中的commons-logging-1.1.1.jar放到lib目錄
????CLASSPATH中添加hibernate-shards.jar、commons-logging-1.1.1.jar
Hibernate Shards測試項目
出于結構上的簡單考慮,定義一個ShardableEntity的接口,讓ContactEntity實現這個接口:
public interface ShardableEntity {public String getIdentifier(); } public class ContactEntity implements ShardableEntity {public String getIdentifier(){return this._id;}//other code omitted is the same with previous post } 映射文件ContactEntity.hbm.xml與上一篇文章中的一樣
因為我們需要將contact的數據以切片的形式存入2個數據庫中,因此需要配置這2個數據庫的信息。Hibernate Shards通過定義2個hibernate的配置文件解決這個問題:
shard0.hibernate.cfg.xml: <?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory><property name="connection.driver_class">com.mysql.jdbc.Driver</property><property name="connection.url">jdbc:mysql://localhost/hbshards</property><property name="connection.username">root</property><property name="connection.password">dev</property><property name="connection.pool_size">10</property><property name="show_sql">true</property><property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property><property name="hbm2ddl.auto">validate</property><property name="hibernate.connection.shard_id">0</property><property name="hibernate.shard.enable_cross_shard_relationship_checks">false</property><mapping resource="ContactEntity.hbm.xml" /> </session-factory> </hibernate-configuration> shard1.hibernate.cfg.xml: <?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory><property name="connection.driver_class">com.mysql.jdbc.Driver</property><property name="connection.url">jdbc:mysql://localhost/hbshards2</property><property name="connection.username">root</property><property name="connection.password">dev</property><property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property><property name="hibernate.connection.shard_id">1</property><property name="hibernate.shard.enable_cross_shard_relationship_checks">false</property> </session-factory> </hibernate-configuration> 其中shard0.hibernate.cfg.xml作為主要的配置文件,針對每個shard創建的SessionFactory對象,除了數據庫連接信息的配置之外,都來自shard0.hibernate.cfg.xml
針對其他每個shard創建的SessionFactory,數據庫連接信息從相應的配置文件(例如shard1.hibernate.cfg.xml)中讀取,讀取的內容包括connection.url、connection.username、connection.password、connection.datasource。為了hibernate加載其他shard的配置文件時不拋異常,在其他shard的配置文件中我們還是把必要的配置屬性寫上去了
hibernate.connection.shard_id屬性為每個shard定義一個id,必須有一個shard_id為0的shard,其他的shard_id可以定義為任意整數
我們假定contact的id都是由數字組成的一個8位長度的字符串
對contact的切割策略簡單定義為: contact id第一個字符為0-4的存入shard0中,為5-9的存入shard1中
為了實現這一策略,我們需要實現hibernate shards的2個接口
ShardResolutionStrategy接口的實現: import java.util.List; import java.util.ArrayList; import org.hibernate.shards.strategy.resolution.ShardResolutionStrategy; import org.hibernate.shards.strategy.selection.ShardResolutionStrategyData; import org.hibernate.shards.ShardId; /** a simple ShardResolutionStrategy implementation for our ContactEntity*/ public class MyShardResolutionStrategy implements ShardResolutionStrategy {private List<ShardId> _shardIds;public MyShardResolutionStrategy(List<ShardId> shardIds){this._shardIds = shardIds;}public List selectShardIdsFromShardResolutionStrategyData(ShardResolutionStrategyData arg0){List ids = new ArrayList();String id = (String)arg0.getId();if(id==null || id.isEmpty()) ids.add(this._shardIds.get(0));else{//our shard selection is identified by the //first char(number) in contact id//0-4 => shards0, 5-9 => shards1Integer i = new Integer(id.substring(0, 1));ids.add(this._shardIds.get(i/5));}return ids;} } ShardSelectionStrategy接口的實現: import java.util.List; import org.hibernate.shards.ShardId; import org.hibernate.shards.strategy.selection.ShardSelectionStrategy; /** a simple ShardSelectionStrategy implementation for our ContactEntity*/ public class MyShardSelectionStrategy implements ShardSelectionStrategy {private List<ShardId> _shardIds;public MyShardSelectionStrategy(List<ShardId> shardIds){this._shardIds=shardIds;}public ShardId selectShardIdForNewObject(Object obj) {if(obj instanceof ShardableEntity) {String id = ((ShardableEntity)obj).getIdentifier();if(id==null || id.isEmpty()) return this._shardIds.get(0);Integer i = new Integer(id.substring(0, 1));//our shard selection is identified by the //first char(number) in contact id//0-4 => shards0, 5-9 => shards1return this._shardIds.get(i/5);}//for non-shardable entities we just use shard0return this._shardIds.get(0);} } 接下來就是怎么使用hibernate shards的測試代碼了: import java.util.Iterator; import java.util.List; import java.util.ArrayList; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.shards.*; import org.hibernate.shards.cfg.*; import org.hibernate.shards.strategy.*; import org.hibernate.shards.strategy.access.*; import org.hibernate.shards.strategy.resolution.*; import org.hibernate.shards.strategy.selection.*;
public class Main {public static void main(String[] args) {HibernateShardsTest(args);}private static SessionFactory createSessionFactory() {//加載主配置文件,為每個shard創建SessionFactory對象時將//以他作為原型Configuration prototypeCfg = new Configuration().configure("shard0.hibernate.cfg.xml");//每個shard的配置文件List<ShardConfiguration> shardCfgs = new ArrayList<ShardConfiguration>();shardCfgs.add(buildShardConfig("shard0.hibernate.cfg.xml"));shardCfgs.add(buildShardConfig("shard1.hibernate.cfg.xml"));//數據切片策略的工廠對象ShardStrategyFactory strategyFactory = buildShardStrategyFactory();ShardedConfiguration shardedConfig = new ShardedConfiguration(prototypeCfg, shardCfgs, strategyFactory);//返回一個ShardedSessionFactory對象return shardedConfig.buildShardedSessionFactory();}private static ShardStrategyFactory buildShardStrategyFactory() {ShardStrategyFactory factory = new ShardStrategyFactory() {//測試用的自定義數據切片策略的工廠類public ShardStrategy newShardStrategy(List<ShardId> shardIds) {ShardSelectionStrategy ss = new MyShardSelectionStrategy(shardIds);ShardResolutionStrategy rs = new MyShardResolutionStrategy(shardIds);ShardAccessStrategy as = new SequentialShardAccessStrategy();return new ShardStrategyImpl(ss, rs, as);}};return factory;}private static ShardConfiguration buildShardConfig(String configFile) {Configuration config = new Configuration().configure(configFile);return new ConfigurationToShardConfigurationAdapter(config);}private static void HibernateShardsTest(String[] args){String loginId = "RicCC@cnblogs.com";String password = "123";if(args!=null && args.length==2){loginId = args[0];password = args[1];}SessionFactory factory = null;try{factory = createSessionFactory();ShardsTestCreate(factory);ShardsTestLogin(factory, loginId, password);ShardsTestDelete(factory);}catch(Exception e){System.out.println(e.getMessage());e.printStackTrace();}finally{if(factory!=null) factory.close();}}private static void ShardsTestCreate(SessionFactory factory){Session session = null;Transaction transaction = null;System.out.println("===Create Contacts===");try{session = factory.openSession();transaction = session.beginTransaction(); session.save(new ContactEntity("01111111","RicCC@cnblogs.com", "123", "Richie", "RicCC@cnblogs.com"));session.save(new ContactEntity("91111111","a@cnblogs.com", "123", "AAA", "a@cnblogs.com"));session.save(new ContactEntity("81111111","b@cnblogs.com", "123", "BBB", "b@cnblogs.com"));session.save(new ContactEntity("31111111","c@cnblogs.com", "123", "CCC", "c@cnblogs.com"));transaction.commit();}catch(Exception e){if(transaction!=null) transaction.rollback();System.out.println(e.getMessage());e.printStackTrace();}finally{if(session!=null) session.close();}}private static void ShardsTestLogin(SessionFactory factory, String loginId, String password){Session session = null;ContactEntity c = null;System.out.println("\n===Login Test===");try{session = factory.openSession();List contacts = session.createQuery("from ContactEntity where LoginId=:loginId").setString("loginId", loginId).list();if(contacts.isEmpty())System.out.println("Contact \"" + loginId + "\" not found!");else{c = (ContactEntity)contacts.get(0);if(c.getPassword().equals(password))System.out.println("Contact \"" + loginId + "\" login successful");else System.out.println("Password is incorrect (should be: "+ c.getPassword() + ", but is: " + password + ")");}System.out.println("\n===Get Contact by Id===");c = (ContactEntity)session.get(ContactEntity.class, "81111111");System.out.println(c.toString());c = (ContactEntity)session.get(ContactEntity.class, "31111111");System.out.println(c.toString());}catch(Exception e){System.out.println(e.getMessage());e.printStackTrace();}finally{if(session!=null) session.close();}}private static void ShardsTestDelete(SessionFactory factory){Session session = null;Transaction transaction = null;System.out.println("\n===Delete Contacts===");try{session = factory.openSession();transaction = session.beginTransaction();List contacts = session.createQuery("from ContactEntity").list();Iterator it = contacts.iterator();while(it.hasNext()){session.delete(it.next());}transaction.commit();}catch(Exception e){if(transaction!=null) transaction.rollback();System.out.println(e.getMessage());e.printStackTrace();}finally{if(session!=null) session.close();}} } 項目結構圖:
?? ?
運行測試
用Eclipse編譯class文件,將生成的所有class文件以及相關的配置、映射文件 拷貝到lib目錄中,運行Main.class結果如下:
D:\Work\research\Java\Hibernate-Test\lib>java Main
===Create Contacts===
Hibernate: insert into CONTACT (EMAIL, NAME, LOGIN_ID, PASSWORD, ID) values (?,?, ?, ?, ?)
Hibernate: insert into CONTACT (EMAIL, NAME, LOGIN_ID, PASSWORD, ID) values (?,?, ?, ?, ?)
Hibernate: insert into CONTACT (EMAIL, NAME, LOGIN_ID, PASSWORD, ID) values (?,?, ?, ?, ?)
Hibernate: insert into CONTACT (EMAIL, NAME, LOGIN_ID, PASSWORD, ID) values (?,?, ?, ?, ?)
===Login Test===
Hibernate: select contactent0_.ID as ID0_, contactent0_.EMAIL as EMAIL0_, contactent0_.NAME as NAME0_, contactent0_.LOGIN_ID as LOGIN4_0_, contactent0_.PASSWORD as PASSWORD0_ from CONTACT contactent0_ where contactent0_.LOGIN_ID=?
Hibernate: select contactent0_.ID as ID0_, contactent0_.EMAIL as EMAIL0_, contactent0_.NAME as NAME0_, contactent0_.LOGIN_ID as LOGIN4_0_, contactent0_.PASSWORD as PASSWORD0_ from CONTACT contactent0_ where contactent0_.LOGIN_ID=?
Contact "RicCC@cnblogs.com" login successful
===Get Contact by Id===
Hibernate: select contactent0_.ID as ID0_0_, contactent0_.EMAIL as EMAIL0_0_, contactent0_.NAME as NAME0_0_, contactent0_.LOGIN_ID as LOGIN4_0_0_, contactent0_.PASSWORD as PASSWORD0_0_ from CONTACT contactent0_ where contactent0_.ID=?
{ Id="81111111", LoginId="b@cnblogs.com", Name="BBB", EMail="b@cnblogs.com" }
Hibernate: select contactent0_.ID as ID0_0_, contactent0_.EMAIL as EMAIL0_0_, contactent0_.NAME as NAME0_0_, contactent0_.LOGIN_ID as LOGIN4_0_0_, contactent0_.PASSWORD as PASSWORD0_0_ from CONTACT contactent0_ where contactent0_.ID=?
{ Id="31111111", LoginId="c@cnblogs.com", Name="CCC", EMail="c@cnblogs.com" }
===Delete Contacts===
Hibernate: select contactent0_.ID as ID0_, contactent0_.EMAIL as EMAIL0_, contactent0_.NAME as NAME0_, contactent0_.LOGIN_ID as LOGIN4_0_, contactent0_.PASSWORD as PASSWORD0_ from CONTACT contactent0_
Hibernate: select contactent0_.ID as ID0_, contactent0_.EMAIL as EMAIL0_, contactent0_.NAME as NAME0_, contactent0_.LOGIN_ID as LOGIN4_0_, contactent0_.PASSWORD as PASSWORD0_ from CONTACT contactent0_
Hibernate: delete from CONTACT where ID=?
Hibernate: delete from CONTACT where ID=?
Hibernate: delete from CONTACT where ID=?
Hibernate: delete from CONTACT where ID=?
1. 通過id加載實體的時候,hibernate shards使用ShardSelectionStrategy來決定應當從哪個shard加載數據
2. 新增數據的時候,hibernate shards使用ShardResolutionStrategy來決定應當將數據insert到哪個shard中
?? ?這里為什么不能使用ShardSelectionStrategy呢?因為與id的生成機制相關,比如說可以讓數據庫來生成id(hibernate中的native方式),例如自增id,在shard的應用場景下可以簡單的給每個shard一個起止范圍,只是在insert數據的時候必須通過特定的算法決定將數據insert到哪個shard中,這樣的情況下ShardSelectionStrategy是無法運用的。而對于assigned等類似的id生成機制,還是可以運用ShardSelectionStrategy策略的
3. 執行hql或者Criteria查詢的時候,則使用ShardAccessStrategy從shard中查詢數據
?? ?上面示例中的buildShardStrategyFactory方法中,我們使用了hibernate shards項目提供的SequentialShardAccessStrategy策略,這個策略在所有shards中逐個執行查詢語句,然后對各個結果進行合并
基于上面幾點,我們可以對上面測試運行后的輸出做出解釋了:
兩個使用了hql的地方,都產生了兩次sql查詢,這是因為使用了SequentialShardAccessStrategy緣故,他從我們定義的兩個shard中分別執行sql查詢,合并結果集返回給調用者
把測試代碼中的刪除語句注釋掉,運行測試后查詢數據庫,可以確認數據都是正確的insert在相應的shard中的
總結
上面演示了hibernate shards的基本用法,總體來看使用上非常簡單,只需要針對實施shard的實體使用hibernate shards提供的ShardedSessionFactory即可
使用hibernate shards也存在一些限制,但大部分都是使用shards后的一些設計要求,或者是目前的it技術背景下對shards設計上的約束,在后面一篇中再詳細整理一下這方面內容
文中使用的測試項目源文件在這里: 下載。引用到的jar文件需要從相關項目網站去下載
總結
以上是生活随笔為你收集整理的Hibernate Shards 数据的水平、垂直切割(二)- Hibernate Shards基本演示的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: One Day-XML:XPath
- 下一篇: Windows server 2003下