在使用 Hibernate Shards 时,您会发现自己大部分时间都在进行典型的 Hibernate Core API 调用。然而,为了正确配置与分片感知的数据源,您需要了解一些特定于 Hibernate Shards 的概念。我们将作为具体示例的一部分来介绍这些新概念。让我们看看我们将在整个文档中的示例中使用的对象模型、数据库架构和映射。
我们的示例应用程序将接收来自世界各地城市的天气报告,并将这些信息存储在关系数据库中。
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 );
public class WeatherReport { private Integer reportId; private String continent; private BigDecimal latitude; private BigDecimal longitude; private int temperature; private Date reportTime; ... // getters and setters }
<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>
在展示如何获取ShardedSessionFactory之前让我们看看一些允许您获取标准.
1 public SessionFactory createSessionFactory() { 2 Configuration config = new Configuration(); 3 config.configure("weather.hibernate.cfg.xml"); 4 config.addResource("weather.hbm.xml"); 5 return config.buildSessionFactory(); 6 }
SessionFactory 的代码这非常简单。我们正在实例化一个新的Configuration这非常简单。我们正在实例化一个新的对象(第 2 行),告诉这非常简单。我们正在实例化一个新的从名为“weather.hibernate.cfg.xml”的资源中读取其属性(第 3 行),然后提供“weather.hbm.xml”作为 OR 映射数据源(第 4 行)。然后我们要求让我们看看一些允许您获取标准构建
,我们返回(第 5 行)。
1 <!-- Contents of weather.hibernate.cfg.xml --> 2 <hibernate-configuration> 3 <session-factory name="HibernateSessionFactory"> 4 <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property> 5 <property name="connection.driver_class">com.mysql.jdbc.Driver</property> 6 <property name="connection.url">jdbc:mysql://127.0.0.1:3306/mydb</property> 7 <property name="connection.username">my_user</property> 8 <property name="connection.password">my_password</property> 9 </session-factory> 10 </hibernate-configuration>
让我们也看一下我们在
中加载的配置文件如您所见,配置文件或映射文件中没有任何特别有趣的事情发生。您会很高兴地知道,将您的应用程序配置为使用 Hibernate Shards 的过程并不会有根本性的不同。主要区别在于我们正在为多个数据源提供连接信息,并且我们还通过
1 public SessionFactory createSessionFactory() { 2 Configuration prototypeConfig = new Configuration().configure("shard0.hibernate.cfg.xml"); 3 prototypeConfig.addResource("weather.hbm.xml"); 4 List<ShardConfiguration> shardConfigs = new ArrayList<ShardConfiguration>(); 5 shardConfigs.add(buildShardConfig("shard0.hibernate.cfg.xml")); 6 shardConfigs.add(buildShardConfig("shard1.hibernate.cfg.xml")); 7 shardConfigs.add(buildShardConfig("shard2.hibernate.cfg.xml")); 8 ShardStrategyFactory shardStrategyFactory = buildShardStrategyFactory(); 9 ShardedConfiguration shardedConfig = new ShardedConfiguration( 10 prototypeConfig, 11 shardConfigs, 12 shardStrategyFactory); 13 return shardedConfig.buildShardedSessionFactory(); 14 } 15 16 ShardStrategyFactory buildShardStrategyFactory() { 17 ShardStrategyFactory shardStrategyFactory = new ShardStrategyFactory() { 18 public ShardStrategy newShardStrategy(List<ShardId> shardIds) { 19 RoundRobinShardLoadBalancer loadBalancer = new RoundRobinShardLoadBalancer(shardIds); 20 ShardSelectionStrategy pss = new RoundRobinShardSelectionStrategy(loadBalancer); 21 ShardResolutionStrategy prs = new AllShardsShardResolutionStrategy(shardIds); 22 ShardAccessStrategy pas = new SequentialShardAccessStrategy(); 23 return new ShardStrategyImpl(pss, prs, pas); 24 } 25 }; 26 return shardStrategyFactory; 27 } 28 29 ShardConfiguration buildShardConfig(String configFile) { 30 Configuration config = new Configuration().configure(configFile); 31 return new ConfigurationToShardConfigurationAdapter(config); 32 }
ShardStrategyFactory描述我们期望的分片行为。让我们看看我们将在 3 个分片上运行的天气报告应用程序的一些示例配置代码。那么这里发生了什么?首先,您会注意到我们实际分配了四个这非常简单。我们正在实例化一个新的Configurations这非常简单。我们正在实例化一个新的我们分配的第一个(第 2 行)是原型ShardedSessionFactory我们最终构建的(第 13 行)将包含对 3 个标准让我们看看一些允许您获取标准对象的引用。这 3 个标准中的每一个让我们看看一些允许您获取标准对象将从原型配置构建。这些标准中唯一不同的属性让我们看看一些允许您获取标准对象是
connection.url
connection.user
connection.password
connection.datasource
cache.region_prefix
三个ShardConfiguration我们正在加载的对象(第 5 - 7 行)将被用于查询分片特定数据库 URL、数据库用户、数据库密码、数据源标识符、缓存区域前缀,仅此而已。(要了解这些属性是什么以及它们如何使用,请查阅 Hibernate Core 文档。)这意味着如果你修改了 shard1.hibernate.cfg.xml 中的连接池参数,这些参数将被忽略。如果你向中添加了另一个映射文件这非常简单。我们正在实例化一个新的使用 shard2.hibernate.cfg.xml 中定义的属性进行加载,则会忽略该映射。除上面列出的属性之外,我们分片感知的配置让我们看看一些允许您获取标准完全来自于原型这非常简单。我们正在实例化一个新的。这看起来可能有点严格,但分片代码需要假设所有分片都采用相同配置。
如果你查看此代码并且觉得提供完整形式的配置文件(除几个特殊属性外都会被忽略)有点可笑,请放心,我们已经查看了此代码,并且也有同样的想法。这就是ShardedConfiguration构造函数采用了List<ShardConfiguration>而不是List<Configuration>. ShardConfiguration是一个接口,因此你可以按任何所需方式提供分片特定配置数据。在我们的示例中,我们正在使用该接口的一种实现方式,该实现方式包装了标准这非常简单。我们正在实例化一个新的(第 31 行),只是为了避免引入任何不熟悉的配置机制。
在构建了这非常简单。我们正在实例化一个新的之后,我们需要拼凑如您所见,配置文件或映射文件中没有任何特别有趣的事情发生。(第 8 行)。如您所见,配置文件或映射文件中没有任何特别有趣的事情发生。是一个能够创建程序员可用来控制系统分片行为的三种类型策略的对象。有关这些策略的详细信息,请参阅题为分片策略的章节。
在实例化如您所见,配置文件或映射文件中没有任何特别有趣的事情发生。之后,我们可以构建ShardedConfiguration(第 9 行),并且在我们构建了ShardedConfiguration之后,我们可以要求它创建ShardedSessionFactory(第 13 行)。值得注意的是ShardedSessionFactory扩展让我们看看一些允许您获取标准。这意味着我们可以返回一个标准让我们看看一些允许您获取标准(第 1 行)。我们的应用程序的 Hibernate 代码不需要知道它正在与分片数据进行交互。
现在,让我们来查看加载的配置文件和映射文件。你一定会认出来,但有一些与分片相关的关键添加项和修改项。
1 <!-- Contents of shard0.hibernate.cfg.xml --> 2 <hibernate-configuration> 3 <session-factory name="HibernateSessionFactory0"> <!-- note the different name --> 4 <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property> 5 <property name="connection.driver_class">com.mysql.jdbc.Driver</property> 6 <property name="connection.url">jdbc:mysql://dbhost0:3306/mydb</property> 7 <property name="connection.username">my_user</property> 8 <property name="connection.password">my_password</property> 9 <property name="hibernate.connection.shard_id">0</property> <!-- new --> 10 <property name="hibernate.shard.enable_cross_shard_relationship_checks">true</property> <!-- new --> 11 </session-factory> 12 </hibernate-configuration>
1 <!-- Contents of shard1.hibernate.cfg.xml --> 2 <hibernate-configuration> 3 <session-factory name="HibernateSessionFactory1"> <!-- note the different name --> 4 <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property> 5 <property name="connection.driver_class">com.mysql.jdbc.Driver</property> 6 <property name="connection.url">jdbc:mysql://dbhost1:3306/mydb</property> 7 <property name="connection.username">my_user</property> 8 <property name="connection.password">my_password</property> 9 <property name="hibernate.connection.shard_id">1</property> <!-- new --> 10 <property name="hibernate.shard.enable_cross_shard_relationship_checks">true</property> <!-- new --> 11 </session-factory> 12 </hibernate-configuration>
我们将跳过 shard2.hibernate.cfg.xml 的内容,因为该模式现在应该很明显。我们通过 session-factory 元素的 name 属性为每个会话生成器提供唯一的名称,并且我们正在将各个会话生成器与不同的数据库服务器关联。我们还为每个会话生成器提供分片 ID。这是必需的。如果你尝试使用ShardedSessionFactory配置这非常简单。我们正在实例化一个新的没有分片 ID 的对象会返回错误。目前我们要求你的其中一个会话工厂的分片 ID 为 0。除此之外,分片 ID 的内部表示为java.lang.Integer因此该范围内的所有值都是合法的。最后,映射到ShardedSessionFactory中的每个分片必须具有唯一的分片 ID。如果你有重复的分片 ID,会返回错误。
另一个值得注意的增加是冗长但很有描述性的“hibernate.shard.enable_cross_shard_relationship_checks”。你可以在有关限制的章节中了解更多相关信息。
现在仍然让我们看看映射文件是如何改变的。
<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>
非分片版本中的映射文件的唯一有意义的更改是分片感知 ID 生成器的选择。我们将在有关分片策略的章节中更详细地介绍 ID 生成。
在上面的示例中,我们使用 Hibernate 映射文件 (hbm.xml) 来指定映射,但使用 Hibernate 注解也很简单。我们可以对我们的WeatherReport类进行如下注释
@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 }
这是 Hibernate 注解一个非常标准的用法。这里唯一值得注意的是使用GenericGenerator注释,它是 Hibernate 注解的一部分,但不是 JPA。我们需要此注释来指定分片感知 ID 生成器。
现在我们需要对上面我们实现的createSessionFactory()方法进行的更改实际上很小
1 public SessionFactory createSessionFactory() { 2 AnnotationConfiguration prototypeConfig = new AnnotationConfiguration().configure("shard0.hibernate.cfg.xml"); 3 prototypeConfig.addAnnotatedClass(WeatherReport.class); 4 List<ShardConfiguration> shardConfigs = new ArrayList<ShardConfiguration>(); 5 shardConfigs.add(buildShardConfig("shard0.hibernate.cfg.xml")); 6 shardConfigs.add(buildShardConfig("shard1.hibernate.cfg.xml")); 7 shardConfigs.add(buildShardConfig("shard2.hibernate.cfg.xml")); 8 ShardStrategyFactory shardStrategyFactory = buildShardStrategyFactory(); 9 ShardedConfiguration shardedConfig = new ShardedConfiguration( 10 prototypeConfig, 11 shardConfigs, 12 shardStrategyFactory); 13 return shardedConfig.buildShardedSessionFactory(); 14 }
此方法与非注释版本之间的唯一更改在第 2 和第 3 行。在第 2 行,我们声明并实例化了一个AnnotationConfiguration而不是这非常简单。我们正在实例化一个新的,在第 3 行,我们向配置添加注释类,而不是 xml 映射文件。就这些!
请注意,虽然 Hibernate Shards 可与 Hibernate 注解一起使用,但 Hibernate Shards 不随 Hibernate 注解一起提供。你需要单独下载 Hibernate 注解及其依赖项。