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

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

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

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

1. 问题情况

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

2. 思路

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

3. 实验

  建表语句:

create table clientinfo  (
   userID               NUMBER(14)                      not null,
   REGION               NUMBER(5)                       not null,
   username             VARCHAR2(16)                    not null,
   age                  number(3)             ,
   address              VARCHAR2(128)         ,
   CHANGEDATE           DATE,
   primary key (userid)
)PARTITION BY LIST(REGION)
( 
PARTITION  clientinfo100 VALUES (100),
PARTITION  clientinfo101 VALUES (101),
PARTITION  clientinfo240 VALUES (270)
);

java代码:

VO类 ClientInfoVO

package com.qingcaolin;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

/**
 * region 库表实际非主键, 该表根据region分区,加入虚拟主键提高查询速度
 * 
 * @author qingcaolin
 * 
 */
@Entity
@IdClass(ClientInfoVOPK.class)
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Table(name = "CLIENTINFO")
public class ClientInfoVO {

		@Id
		@Column(name = "userId")
		private Long userId;
		@Id
		@Column(name = "REGION")
		private Long region;

		@Column(name = "USERNAME")
		private String userName;

		@Column(name = "AGE")
		private Long age;

		@Column(name = "address")
		private String address;

		@Column(name = "CHANGEDATE")
		private Date changedate;

		public ClientInfoVO(){}
		
		public ClientInfoVOPK getPk() {
			ClientInfoVOPK pk = new ClientInfoVOPK();
			pk.setUserId(userId);
			pk.setRegion(region);
			return pk;
		}
PK类:
package com.qingcaolin;

import java.io.Serializable;

public class ClientInfoVOPK implements Serializable {

	private Long userId;
	private Long region;
	public ClientInfoVOPK(){
		
	}

	public int hashCode() {
		return (int) userId.hashCode();
	}

	public boolean equals(Object obj) {
		if (obj == this)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof ClientInfoVOPK))
			return false;
		ClientInfoVOPK pk = (ClientInfoVOPK) obj;
		return pk.userId == userId;
	}


测试Main类:

package com.qingcaolin;


import java.util.Date;


import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;


public class TestMain {
	public static void main(String[] args) {
		SessionFactory sf = new AnnotationConfiguration().addAnnotatedClass(ClientInfoVO.class).configure()
				.buildSessionFactory();
		
		{//新增
			Session session = sf.openSession();
			Transaction tx = session.beginTransaction();
			ClientInfoVO infoVO = new ClientInfoVO();
			infoVO.setUserId(223L);
			infoVO.setRegion(270L);
			infoVO.setAge(18L);
			infoVO.setUserName("qingcaolin");
			infoVO.setAddress("武汉市");
			infoVO.setChangedate(new Date());
			session.save(infoVO);
			tx.commit();
			session.close();
		}
		{	//查询
			Session querySession = sf.openSession();
			Transaction queryTx = querySession.beginTransaction();
			ClientInfoVO queryVO = new ClientInfoVO();
			queryVO.setUserId(223L);
			queryVO.setRegion(270L);
			
			queryVO=(ClientInfoVO) querySession.get(ClientInfoVO.class,queryVO.getPk());
			System.out.println(queryVO.getUserId()+","+queryVO.getRegion()+","+queryVO.getAge()+","
					+queryVO.getUserName()+","+queryVO.getAddress()+","+queryVO.getChangedate());
			queryVO.setAge(28L);
			queryVO.setUserName("qingcaolin---");
			queryVO.setAddress("武汉市--");
			queryVO.setChangedate(new Date());
			querySession.update(queryVO);
			queryTx.commit();
			querySession.close();
		}
	}
}

控制台打印的日志:

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的约束, 应用时可能存在问题, 建议寻找合适的解决方法。



                  欢迎加入讨论。转载请写明出处。谢谢!!





评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值