Hibernate Shards

A Wikipédiából, a szabad enciklopédiából

A Hibernate Shards egy olyan keretrendszer, amely olyan problémákra nyújt megoldást, ahol az összes szükséges adatot nem tudjuk egy relációs adatbázisban eltárolni. Az adatok egységbe zárhatók, minimalizálva ezen műveletek komplexitását, támogatást nyújtva a horizontális particionálás megvalósítására a Hibernate Core-ban.

Használata abban az esetben javasolt például, amikor túl sok adattal, vagy elosztott architektúrán kell dolgozni. Ilyen esetekben több elosztott adatbázisra lehet szükség, amely kétségkívül bonyolítja egy alkalmazás fejlesztését.

A shard szó jelentése cserép, szilánk, a Hibernate Shards felfogható, mint egy keretrendszer, mely ezen külön álló szilánkokat egyesíti, hogy a fejlesztő egységként tudja kezelni őket.

Legfőbb tulajdonságai[szerkesztés]

  • Sztenderd Hibernate programozási modell: lehetővé teszi, hogy az eddig használt Hibernate API (pl. SessionFactory, Session, Criteria,Query) a szokásos módon használható legyen. Azaz aki ismerte a Hibernate használatát, már ismeri a Hibenate Shards használatát is.
  • Rugalmas sharding (szilánkolási) stratégiák: az adatok szabadon eloszthatók a szilánkok közt. Ehhez használhatók a kész stratégiák, vagy egy a felhasználó által definiált saját stratégia.
  • Virtuális shard-ok (szilánkok) támogatása: Amennyiben a szilánkolási stratégia változik, az új szilánkok hozzáadása és rajtuk az adatok elosztása az nehéz feladat lehet, ám a Hibernate Shards támogatja a virtuális shard-ok (szilánkok) létrehozását, amivel az adatok újraelosztása egyszerűsíthető.
  • Ingyenes / nyílt forráskódú: A Hibernate Shards a LGPL (Lesser GNU Public License) égisze alatt bejegyezett termék

Példa[szerkesztés]

Az alábbi példa a világ minden pontjáról érkező időjárási adatokat dolgoz fel és tárol el egy relációs adatbázisban:

Az adatbázissémát a következő sql definiálja:

 CREATE TABLE WEATHER_REPORT (
    REPORT_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    CONTINENT ENUM('AFRICA', 'ANTARCTICA', 'ASIA', 'AUSTRALIA', 'EUROPE', 'NORTH AMERICA', 'SOUTH AMERICA'),
    LATITUDE FLOAT,
    LONGITUDE FLOAT,
    TEMPERATURE INT,
    REPORT_TIME TIMESTAMP
 );

Egy “jelentés” modellje:

 public class WeatherReport {
    private Integer reportId;
    private String continent;
    private BigDecimal latitude;
    private BigDecimal longitude;
    private int temperature;
    private Date reportTime;
    ... // getters and setters
 }

A weather.hbm.xml tartalma a következő:

 <hibernate-mapping package="org.hibernate.shards.example.model">
    <class name="WeatherReport" table="WEATHER_REPORT">
        <id name="reportId" column="REPORT_ID">
            <generator class="native"/>
        </id>
        <property name="continent" column="CONTINENT"/>
        <property name="latitude" column="LATITUDE"/>
        <property name="longitude" column="LONGITUDE"/>
        <property name="temperature" column="TEMPERATURE"/>
        <property name="reportTime" type="timestamp" column="REPORT_TIME"/>
    </class>
 </hibernate-mapping>

Hozzáférés a SharedSessionFactory-hoz:

Az alábbi kódrészlet az időjárás jelentő alkalmazásból 3 szilánkot használ:

 public SessionFactory createSessionFactory() {
        Configuration prototypeConfig = new Configuration().configure("shard0.hibernate.cfg.xml");
        prototypeConfig.addResource("weather.hbm.xml");
        List<ShardConfiguration> shardConfigs = new ArrayList<ShardConfiguration>();
        shardConfigs.add(buildShardConfig("shard0.hibernate.cfg.xml"));
        shardConfigs.add(buildShardConfig("shard1.hibernate.cfg.xml"));
        shardConfigs.add(buildShardConfig("shard2.hibernate.cfg.xml"));
        ShardStrategyFactory shardStrategyFactory = buildShardStrategyFactory();
        ShardedConfiguration shardedConfig = new ShardedConfiguration(
            prototypeConfig,
            shardConfigs,
            shardStrategyFactory);
        return shardedConfig.buildShardedSessionFactory();
    }

    ShardStrategyFactory buildShardStrategyFactory() {
        ShardStrategyFactory shardStrategyFactory = new ShardStrategyFactory() {
            public ShardStrategy newShardStrategy(List<ShardId> shardIds) {
                RoundRobinShardLoadBalancer loadBalancer = new RoundRobinShardLoadBalancer(shardIds);
                ShardSelectionStrategy pss = new RoundRobinShardSelectionStrategy(loadBalancer);
                ShardResolutionStrategy prs = new AllShardsShardResolutionStrategy(shardIds);
                ShardAccessStrategy pas = new SequentialShardAccessStrategy();
                return new ShardStrategyImpl(pss, prs, pas);
            }
        };
        return shardStrategyFactory;
    }

    ShardConfiguration buildShardConfig(String configFile) {
        Configuration config = new Configuration().configure(configFile);
        return new ConfigurationToShardConfigurationAdapter(config);
    }

A fenti kódrészletből kiderül, hogy valójában 4 Configuration számára foglalunk helyet. Az első a prototípus, a létrehozott ShardSessionFactory pedig a 3 sztenderd SessionFactory objektum referenciáit tárolja. Ezek közül mind három a prototípus konfigurációját hordozza. Mindössze pár attribútumukban térnek el:

  • Connection.url
  • Connection.user
  • Connection.password
  • Connection.datasource
  • Cache.region_prefix

A 3 ShardConfiguration objektum shard-specifikus adatbázis url, adatbázis felhasználó, adatbázis kód, adatforrás azonosító és cache-régió prefix-et után fog nézni. Ez azt jelenti, hogy még ha meg is változtatjuk a kapcsolódási paramétereket a shard1.hibernate.xml-ben, a változtatások figyelmen kívül lesznek hagyva. Minden beállítás a fent felsoroltakat leszámítva a prototípus Configuration-ből származik. A Configuration objektum létrehozása után össze kell raknunk egy ShardStrategyFactory-t. Ezután következhet a ShardedConfiguration elkészítése, melynek befejeztével kérhetjük a ShardedSessionFactory létrehozását. Fontos megjegyezni, hogy a ShardedSessionFactory a SessionFactory-ből származik.

Ezután vessünk egy pillantást a configuration és mapping fájlokra:

 <!-- Contents of shard0.hibernate.cfg.xml -->
     <hibernate-configuration>
       <session-factory name="HibernateSessionFactory0"> <!-- note the different name -->
         <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
         <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
         <property name="connection.url">jdbc:mysql://dbhost0:3306/mydb</property>
         <property name="connection.username">my_user</property>
         <property name="connection.password">my_password</property>
         <property name="hibernate.connection.shard_id">0</property> <!-- new -->
         <property name="hibernate.shard.enable_cross_shard_relationship_checks">true</property> <!-- new -->
     </session-factory>
   </hibernate-configuration>

 <!-- Contents of shard1.hibernate.cfg.xml -->
     <hibernate-configuration>
       <session-factory name="HibernateSessionFactory1"> <!-- note the different name -->
         <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
         <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
         <property name="connection.url">jdbc:mysql://dbhost1:3306/mydb</property>
         <property name="connection.username">my_user</property>
         <property name="connection.password">my_password</property>
         <property name="hibernate.connection.shard_id">1</property> <!-- new -->
         <property name="hibernate.shard.enable_cross_shard_relationship_checks">true</property> <!-- new -->
      </session-factory>
   </hibernate-configuration>

A shard2.hibernate.cfg.xml tartalmát nem részletezzük, az a fentiekből egyértelműen következik. Mindegyik session factory-nek egyedi nevet adunk a session-factory elem név attribútumával. Mindegyik session factory-hoz másik adatbázis szervert adunk meg. Ezen kívúl a session factory-knak adunk egy egyedi shard id-t. Ezután a mapping file megváltozik:

 <hibernate-mapping package="org.hibernate.shards.example.model">
    <class name="WeatherReport" table="WEATHER_REPORT">
        <id name="reportId" column="REPORT_ID" type="long">
            <generator class="org.hibernate.shards.id.ShardedTableHiLoGenerator"/>
        </id>
        <property name="continent" column="CONTINENT"/>
        <property name="latitude" column="LATITUDE"/>
        <property name="longitude" column="LONGITUDE"/>
        <property name="temperature" column="TEMPERATURE"/>
        <property name="reportTime" type="timestamp" column="REPORT_TIME"/>
    </class>
 </hibernate-mapping>

A Hibernate annotációk használata shard-okkal[szerkesztés]

A fenti példában Hibernate mapping fájlokat használtunk, hogy a mapping-eket specifikáljuk, de ugyanezt megtehetjük Hibernate annotációkkal is:

 @Entity
 @Table(name="WEATHER_REPORT")
 public class WeatherReport {
    @Id @GeneratedValue(generator="WeatherReportIdGenerator")
    @GenericGenerator(name="WeatherReportIdGenerator", strategy="org.hibernate.shards.id.ShardedUUIDGenerator")
    @Column(name="REPORT_ID")
    private Integer reportId;
    @Column(name="CONTINENT")
    private String continent;
    @Column(name="LATITUDE")
    private BigDecimal latitude;
    @Column(name="LONGITUDE")
    private BigDecimal longitude;
    @Column(name="TEMPERATURE")
    private int temperature;
    @Column(name="REPORT_TIME")
    private Date reportTime;
    ... // getters and setters
 }

A fenti példa az annotációk használatára egy egyszerű példa. Ezután csak pár egyszerű változtatásra van szükség a createSessionFactory metódusban:

  public SessionFactory createSessionFactory() {
          AnnotationConfiguration prototypeConfig = new AnnotationConfiguration().configure("shard0.hibernate.cfg.xml");
          prototypeConfig.addAnnotatedClass(WeatherReport.class);
          List<ShardConfiguration> shardConfigs = new ArrayList<ShardConfiguration>();
          shardConfigs.add(buildShardConfig("shard0.hibernate.cfg.xml"));
          shardConfigs.add(buildShardConfig("shard1.hibernate.cfg.xml"));
          shardConfigs.add(buildShardConfig("shard2.hibernate.cfg.xml"));
          ShardStrategyFactory shardStrategyFactory = buildShardStrategyFactory();
          ShardedConfiguration shardedConfig = new ShardedConfiguration(
               prototypeConfig,
               shardConfigs,
               shardStrategyFactory);
           return shardedConfig.buildShardedSessionFactory();
       }