Hibernate巧用虚拟主键支持Oracle分区

大型的系统,数据量上升到一定程度,都会选择对库表进行切分,会根据表记录的性质,进行分表、或者表分区处理。  最近在系统升级,库表重新设计过程中,就遇到了分表、分区情况。

      分表的处理比较简单,根据一定的规则,识别分表编号, 对对应的VO(Bean)进行处理即可。

       对于分区,本来应该比分表更简单, 因为Oracle的分区,对用户是透明的,用户本可以不用理会,程序无需改造即可。 但为了提高数据库查询效率,在查询和修改的条件中,需要加上分区字段,这样oracle特有的优化技术就能识别分区条件,提高效率。

1. 问题情况

    在电信行业中,数据往往根据用户的区域进行分区存储,现有表ClientInfo, 根据其对应的Region 字段进行列值分区,在对应的查询和修改过程中, where条件中需要region字段值,这样有助于提高查询速度。现在问题就是: 有的表分区,有的表不分区, 如何在改动系统最小情况下, 让系统支持分区字段。

2. 思路

    在网上百度了一把,没有找到合适的解决方案, 有些推荐Google的hibernate Shards 分区包,粗略的了解了下,不太适合。 其实目前的情况很简单,就是一般查询时,在Where条件后增加分区字段 而尽量不改动程序。 想过 Hibernate 事件监听,感觉太复杂, 最终,想到了一个觉得可行的方法, 将分区字段加入到 主键中。 理由:分区字段是非空的、查询更新时与主键一起出现, 觉得没有问题,就开始动手实验。

3. 实验

  建表语句:

[sql]  view plain  copy
  1. create table clientinfo  (  
  2.    userID               NUMBER(14)                      not null,  
  3.    REGION               NUMBER(5)                       not null,  
  4.    username             VARCHAR2(16)                    not null,  
  5.    age                  number(3)             ,  
  6.    address              VARCHAR2(128)         ,  
  7.    CHANGEDATE           DATE,  
  8.    primary key (userid)  
  9. )PARTITION BY LIST(REGION)  
  10. (   
  11. PARTITION  clientinfo100 VALUES (100),  
  12. PARTITION  clientinfo101 VALUES (101),  
  13. PARTITION  clientinfo240 VALUES (270)  
  14. );  

Java代码:

VO类 ClientInfoVO

[java]  view plain  copy
  1. package com.qingcaolin;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import javax.persistence.Column;  
  6. import javax.persistence.Entity;  
  7. import javax.persistence.Id;  
  8. import javax.persistence.IdClass;  
  9. import javax.persistence.Inheritance;  
  10. import javax.persistence.InheritanceType;  
  11. import javax.persistence.Table;  
  12.   
  13. /** 
  14.  * region 库表实际非主键, 该表根据region分区,加入虚拟主键提高查询速度 
  15.  *  
  16.  * @author qingcaolin 
  17.  *  
  18.  */  
  19. @Entity  
  20. @IdClass(ClientInfoVOPK.class)  
  21. @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)  
  22. @Table(name = "CLIENTINFO")  
  23. public class ClientInfoVO {  
  24.   
  25.         @Id  
  26.         @Column(name = "userId")  
  27.         private Long userId;  
  28.         @Id  
  29.         @Column(name = "REGION")  
  30.         private Long region;  
  31.   
  32.         @Column(name = "USERNAME")  
  33.         private String userName;  
  34.   
  35.         @Column(name = "AGE")  
  36.         private Long age;  
  37.   
  38.         @Column(name = "address")  
  39.         private String address;  
  40.   
  41.         @Column(name = "CHANGEDATE")  
  42.         private Date changedate;  
  43.   
  44.         public ClientInfoVO(){}  
  45.           
  46.         public ClientInfoVOPK getPk() {  
  47.             ClientInfoVOPK pk = new ClientInfoVOPK();  
  48.             pk.setUserId(userId);  
  49.             pk.setRegion(region);  
  50.             return pk;  
  51.         }  
[java]  view plain  copy
  1. PK类:  
[java]  view plain  copy
  1. package com.qingcaolin;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. public class ClientInfoVOPK implements Serializable {  
  6.   
  7.     private Long userId;  
  8.     private Long region;  
  9.     public ClientInfoVOPK(){  
  10.           
  11.     }  
  12.   
  13.     public int hashCode() {  
  14.         return (int) userId.hashCode();  
  15.     }  
  16.   
  17.     public boolean equals(Object obj) {  
  18.         if (obj == this)  
  19.             return true;  
  20.         if (obj == null)  
  21.             return false;  
  22.         if (!(obj instanceof ClientInfoVOPK))  
  23.             return false;  
  24.         ClientInfoVOPK pk = (ClientInfoVOPK) obj;  
  25.         return pk.userId == userId;  
  26.     }  


测试Main类:

[java]  view plain  copy
  1. package com.qingcaolin;  
  2.   
  3.   
  4. import java.util.Date;  
  5.   
  6.   
  7. import org.hibernate.Session;  
  8. import org.hibernate.SessionFactory;  
  9. import org.hibernate.Transaction;  
  10. import org.hibernate.cfg.AnnotationConfiguration;  
  11.   
  12.   
  13. public class TestMain {  
  14.     public static void main(String[] args) {  
  15.         SessionFactory sf = new AnnotationConfiguration().addAnnotatedClass(ClientInfoVO.class).configure()  
  16.                 .buildSessionFactory();  
  17.           
  18.         {//新增  
  19.             Session session = sf.openSession();  
  20.             Transaction tx = session.beginTransaction();  
  21.             ClientInfoVO infoVO = new ClientInfoVO();  
  22.             infoVO.setUserId(223L);  
  23.             infoVO.setRegion(270L);  
  24.             infoVO.setAge(18L);  
  25.             infoVO.setUserName("qingcaolin");  
  26.             infoVO.setAddress("武汉市");  
  27.             infoVO.setChangedate(new Date());  
  28.             session.save(infoVO);  
  29.             tx.commit();  
  30.             session.close();  
  31.         }  
  32.         {   //查询  
  33.             Session querySession = sf.openSession();  
  34.             Transaction queryTx = querySession.beginTransaction();  
  35.             ClientInfoVO queryVO = new ClientInfoVO();  
  36.             queryVO.setUserId(223L);  
  37.             queryVO.setRegion(270L);  
  38.               
  39.             queryVO=(ClientInfoVO) querySession.get(ClientInfoVO.class,queryVO.getPk());  
  40.             System.out.println(queryVO.getUserId()+","+queryVO.getRegion()+","+queryVO.getAge()+","  
  41.                     +queryVO.getUserName()+","+queryVO.getAddress()+","+queryVO.getChangedate());  
  42.             queryVO.setAge(28L);  
  43.             queryVO.setUserName("qingcaolin---");  
  44.             queryVO.setAddress("武汉市--");  
  45.             queryVO.setChangedate(new Date());  
  46.             querySession.update(queryVO);  
  47.             queryTx.commit();  
  48.             querySession.close();  
  49.         }  
  50.     }  
  51. }  

控制台打印的日志:

11-15 23:05:49 - Not binding factory to JNDI, no JNDI name configured
Hibernate: insert into CLIENTINFO (address, AGE, CHANGEDATE, USERNAME, region, userId) values (?, ?, ?, ?, ?, ?)
Hibernate: select clientinfo0_.region as region0_0_, clientinfo0_.userId as userId0_0_, clientinfo0_.address as address0_0_, clientinfo0_.AGE as AGE0_0_, clientinfo0_.CHANGEDATE as CHANGEDATE0_0_, clientinfo0_.USERNAME as USERNAME0_0_ from CLIENTINFO clientinfo0_ where clientinfo0_.region=? and clientinfo0_.userId=?
223,270,18,qingcaolin,武汉市,2012-11-15 23:05:49.0

Hibernate: update CLIENTINFO set address=?, AGE=?, CHANGEDATE=?, USERNAME=? where region=? and userId=?

根据日志显示, 在查询和更新的时候, region都自动的加入到where条件。 测试通过,方案可行。


4.缺陷

   对于上面的方案,有个明显的缺陷就是, 将分区字段region加入到虚拟主键中, Hibernate会认为主键为联合主键, 联合主键 会有一些约束,例如,Hibernate无法为联合主键中的列 自动读取sequence赋值。

 因此,上面的方案无法应用于主键为 sequence自动生成的表, 还是存在一定的缺陷。  在实际应用中,也遇到过,在我们的系统中,这种sequence为主键的表,一般为日志 或者 查询为手写hql情况,需要自己补充条件。

具体情况具体分析, 如果 您有更好的方案 或者其他问题,欢迎一起讨论。

5.总结

   将分区字段虚拟的加入 主键, 能比较方便的解决Hibernate支持分区问题, 对于Oracle的列值,范围分区 能显著提高性能。受限于Hibernate 的多主键 对于sequence的约束, 应用时可能存在问题, 建议寻找合适的解决方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Hibernate 中配置联合,可以通过使用 @IdClass 或 @EmbeddedId 注解来实现。 1. 使用 @IdClass 注解: 首先,需要创建一个用于表示联合的类,该类需要实现 Serializable 接口,并且需要包含所有联合字段。例如: ``` public class MyCompositeKey implements Serializable { private Long id1; private Long id2; // constructors, getters, setters, equals, hashCode, etc. } ``` 然后,在实体类中使用 @IdClass 注解,并指定联合类的名称。例如: ``` @Entity @IdClass(MyCompositeKey.class) public class MyEntity { @Id private Long id1; @Id private Long id2; // other entity fields, getters, setters, etc. } ``` 2. 使用 @EmbeddedId 注解: 首先,需要创建一个用于表示联合的类,该类需要实现 Serializable 接口,并且需要使用 @Embeddable 注解标记。例如: ``` @Embeddable public class MyCompositeKey implements Serializable { private Long id1; private Long id2; // constructors, getters, setters, equals, hashCode, etc. } ``` 然后,在实体类中使用 @EmbeddedId 注解,并指定联合类的名称。例如: ``` @Entity public class MyEntity { @EmbeddedId private MyCompositeKey id; // other entity fields, getters, setters, etc. } ``` 以上就是在 Hibernate 中配置联合的两种方式。需要注意的是,使用 @IdClass 注解时需要在实体类中指定所有联合字段,而使用 @EmbeddedId 注解时只需要在联合类中指定即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值