Hibernate Shards

A Wikipédiából, a szabad enciklopédiából
A lap korábbi változatát látod, amilyen Tudor987 (vitalap | szerkesztései) 2015. december 16., 02:57-kor történt szerkesztése után volt. Ez a változat jelentősen eltérhet az aktuális változattól.

A Hibernate Shards egy olyan keretrendszer, amely olyan problémákra nyújt megoldást, ahol ö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

  • 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

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

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();
       }