gaobudong 2010-11-24
大家有没有遇到过这样的情况,一个Entity对应多个相同结构的数据库表。
打个比方,
我们需要根据时间划分到同一个数据库结构相同的多个表中(情况1)
我们需要把数据分散在不同的数据库结构相同的多个表中(情况2)
而情况二又分为以下两种情况:
以一个与该Entity的业务逻辑无关的项作为分拆条件(情况2.1)
以一个与该Entity的业务逻辑有关的项作为分拆条件(情况2.2)
而2.2就是我要实现的情况。我们来单独一个一个讲解实现方法:
这个是老大先让我看的技术Hibernate Shards,上帝啊,这个manual也是纯英文的,差点没把我看死。
最终发现她的实现方法就是通过不同的数据源配置以及strategy的自己实现来完成对于数据的分布式存储过程。
http://www.hibernate.org/subprojects/shards.html
有兴趣的同学可以自己阅读以下他的手册~可惜不能满足我的需求,于是就没有实现。
简单的介绍如下:
1 <!-- Contents of shard0.hibernate.cfg.xml -->
2<hibernate-configuration>
3<session-factoryname="HibernateSessionFactory0"><!--notethedifferentname-->
4<propertyname="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
5<propertyname="connection.driver_class">com.mysql.jdbc.Driver</property>
6<propertyname="connection.url">jdbc:mysql://dbhost0:3306/mydb</property>
7<propertyname="connection.username">my_user</property>
8<propertyname="connection.password">my_password</property>
9<propertyname="hibernate.connection.shard_id">0</property><!--new-->
10<propertyname="hibernate.shard.enable_cross_shard_relationship_checks">true</property><!--11</session-factory>
12 </hibernate-configuration>在配置sessionfactory的时候对于不同的数据库配置不同的shard_id方便之后的分配过程。
public interface ShardStrategy {
ShardSelectionStrategygetShardSelectionStrategy();
ShardResolutionStrategygetShardResolutionStrategy();
ShardAccessStrategygetShardAccessStrategy();
}以上为shard提供的一个接口,需要我们自己的需求进行实现,比如我需要根据不同的大陆保存在不同的数据库的同名表中,那么在ShardSelectionStrategy的实现当中就可以这样实现:
public class WeatherReportShardSelectionStrategy implements ShardSelectionStrategy {
publicShardIdselectShardIdForNewObject(Objectobj){
if(objinstanceofWeatherReport){
return((WeatherReport)obj).getContinent().getShardId();
}
thrownewIllegalArgumentException();
}
}只需要自己再continent类当中指定不同的Continent应该放在哪个数据库里就好(Shard_id来指定)。
别忘了要把所有的接口都用自己的分配逻辑实现哦。
public class WeatherReportShardResolutionStrategy extends AllShardsShardResolutionStrategy {
publicWeatherReportShardResolutionStrategy(List<ShardId>shardIds){
super(shardIds);
}
publicList<ShardId>selectShardIdsFromShardResolutionStrategyData(
ShardResolutionStrategyDatasrsd){
if(srsd.getEntityName().equals(WeatherReport.class.getName())){
returnContinent.getContinentByReportId(srsd.getId()).getShardId();
}
returnsuper.selectShardIdsFromShardResolutionStrategyData(srsd);
}
}但是由于这个分布式存储和读取的方法现在还是初级阶段,还不能支持排序和distinct这样的算法。
因为比如select distinct(name) from Student 这样的方法,Shards会先后查询所有的数据库中的STUDENT表,在查询过程中他只能支持每个表的Distinct,但是所有的结果集中的distinct当前版本还是没有支持。
真的希望他能够在之后的版本中尽快支持,因为这个可能这些缺陷可能是用户不使用他的一个最大的理由。
对于这个需求的实现,Hibernate自己就可以通过配置hibernate自身的strategy实现。
关于namingstrategy,我想我直接贴一个别人的贴子就可以了。
里面讲的很详细,我概括一下就是根据一些业务无关的属性来动态修改entity对应的表关系
比如我们可以根据时间的不同来动态指派数据表:
我们需要数据库表动态根据年份来划分,希望数据库表名为SSE_STUDENT_xxxx(年份)。就可以用以上的技术来实现。
public class MyNamingStrategy extends DefaultNamingStrategy {
publicstaticfinalMyNamingStrategyINSTANCE=newMyNamingStrategy();
publicStringclassToTableName(StringclassName){
return"SSE_"+className.toUpperCase()+"_"+Calendar.getInstance().get(Calendar.YEAR);
}
}具体的大家可以看看我引用的帖子。
不知道其它的大大怎么实现这个需求的,我的实现方法是使用继承来实现,用一个父类让他拥有所有需要的属性,让每个不同的子类单纯的继承他,而没有任何多余的属性。我的实现分为用配置文件完成和用annotation(注解)实现两种方法。
配置文件方法:
单纯的生成一个拥有id,name,type和description的实体类BaseClass
public class BaseClass {
protectedLongid;
protectedStringname;
protectedStringtype;
protected String description;//getters and setters
}
然后让子类继承他:
public class ChildClassA extends BaseClass {}
public class ChildClassB extends BaseClass {}
在BaseClass.hbm.xml中使用union-subclass来继承父类,而让父类声明为abstract:
<class name="com.ibm.cdl.ospf.dynamicshift.dao.bean.BaseClass"
abstract="true">
<idname="id"type="java.lang.Long">
<columnname="ID"/>
<generatorclass="increment"/>
</id>
<propertyname="name"type="java.lang.String">
<columnname="NAME"length="64"/>
</property>
<propertyname="type"type="java.lang.String">
<columnname="TYPE"length="64"/>
</property>
<propertyname="description"type="java.lang.String">
<columnname="DESCRIPTION"/>
</property>
<union-subclassname="com.ibm.cdl.ospf.dynamicshift.dao.bean.ChildClassA"table="DYNAMIC_SHIFT_A_TABLE">
</union-subclass>
<union-subclassname="com.ibm.cdl.ospf.dynamicshift.dao.bean.ChildClassB"table="DYNAMIC_SHIFT_B_TABLE">
</union-subclass>
</class>在hibernate.cfg.xml中这样写上mapping就搞定了~~
<mapping resource="com/ibm/cdl/ospf/dynamicshift/dao/bean/map/BaseClass.hbm.xml" />
简单吧,重点就在union-subclass来实现,如果需要再写一些工具类来让子类间进行转化或者管理都可以。
接下来是annotation版的:
先是实体类:
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
publicclassAnnotationBaseClass{
@Id
protectedLongid;
@Column(name="NAME")
protectedStringname;
@Column(name="TYPE")
protectedStringtype;
@Column(name="DESCRIPTION")
protected String description;//getters and setters
}
注意这一句:@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS),即申明一个类一个数据库表。
然后只要在子类中单纯的继承,然后使用@Entity然后声明具体的数据库表就可以了。
@Entity
@Table(name="DYNAMIC_SHIFT_A_TABLE")
public class AnnotationChildClassA extends AnnotationBaseClass {}在hibernate.cfg.xml中则是声明上具体的class mapping就可以了:
<mapping class="com.ibm.cdl.ospf.dynamicshift.dao.bean.AnnotationChildClassA"/><mapping class="com.ibm.cdl.ospf.dynamicshift.dao.bean.AnnotationBaseClass"/>
希望以上的对大家有帮助~谢谢支持。