作者:caocao(网络隐士),
http://www.caocao.name,
http://www.caocao.mobi
转载请注明来源:
http://www.iteye.com/topic/142404
由于项目需要隐士最近在.Net下面搞NHibernate来实现分表操作,参考了大量资料和JavaEye论坛上火热的讨论(这里谢过各位高人),结合对NHibernate代码的研读,隐士找到了一个简单的实现方式,由于NHibernate和Hibernate同宗同源,隐士觉得这个简单的实现方式同样可以适用于Java环境里的Hibernate,所以下面的代码是C#的。如果管理员觉得不适合发在Java版,请转.Net版。
先讨论一下论坛上讨论过的已有做法:
1、用JDBC直接开搞,出处:
http://www.iteye.com/topic/133832
如果直接搞,就没有必要用Hibernate了。隐士要找的是在NHibernate框架下实现分表的所有操作。
2、对每个表建模,出处:
http://www.iteye.com/topic/133832
如果是几十张表,建几十个模,累死,代码还很不好写,将来的维护也是个大问题。如果要增加一张表,代码可能要改死。
3、直接构造select来实现读取,出处:
http://www.iteye.com/topic/29514
仅仅实现了select,而且必须把表名写死,没有实现其它操作,也不利于开发和维护。
4、Hibernate 3.0里面的dynamic models可能可以实现,出处:
http://www.iteye.com/topic/13167
这个没有研究过,加上NHibernate还没有跟上Hibernate 3.0,所以没有该功能,也无法研究。
再讨论一下隐士的几个思路:
1、实现MultiTablesEntityPersister
在hbm.xml的class里可以指定persister来加载自己实现的persister,是不是可以实现MultiTablesEntityPersister来掌控全局呢?经过隐士大量试验表明几乎不可能,Hibernate认准了一个class对应一张table,大量代码在AbstractEntityPersister里写死了,如果要实现分表需求,基本相当于要重写小半个Hibernate。隐士决定另找出路。
2、实现SessionFactory,Session,Table
如果可以通过继承实现SessionFactory,Session,Table来实现分表需求,那也不错。经隐士研究源码,发现没有希望,接口都定死了,一些关键部分被seal,private,internal了,类似Java里的final,private,anonymous。除非改NHibernate源码,这是隐士所不希望的,这样改开源的源码实在是不应该。
几个思路都被否决后,隐士转向拿Configuration开刀,毕竟hbm.xml里的配置是在Configuration里解析的。一阵分析后发现Configuration把解析工作外包给HbmBinder,在HbmBinder里隐士找到了这句:
tableName = mappings.NamingStrategy.TableName(tableNode.Value);
哈哈,这句就是万恶之源了,原来可以通过Configuration.SetNamingStrategy(INamingStrategy namingStrategy)来注入我们自己的命名规范。隐士想到此处眼前豁然开朗,只要在Configuration.BuildSessionFactory前注入NamingStrategy,搞出来的SessionFactory就对分表这件事根本不知道,而且对于特定的class只认特定的table。不过也带来一个副作用,就是有多少个分表,就要准备多少个SessionFacotry,再想想未必是副作用,SessionFactory维持的缓存就不会跨表打架,可以说这个想法是解决得很不错的。
接着隐士动手开始试验,以下代码基于NHibernate-1.2.0.GA,MySQL 5.0,不过对于Java的Hibernate几乎可以原封不动拿来用,这步留待看官们自己做了。
隐士随便写了个系统负载表,里面放几个字段。这表也有实际意义,比如有一台机器用来集中监控几十台机器,监控数据都放在一张表里会慢死的,一台机器一张表,干净。
CREATE TABLE IF NOT EXISTS `system_1_loads` (
`loggingDate` datetime NOT NULL default '2006-01-01 00:00:00',
`cpuUsage` float NOT NULL default '0',
`memoryUsage` float NOT NULL default '0',
`bytesReceivedPerSecond` int(32) default '0',
`bytesSentPerSecond` int(32) default '0',
PRIMARY KEY (`loggingDate`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
... system_2_loads ... system_3_loads ... ... system_10_loads
hibernate.cfg.xml照常规配置,一点都不需要改动,这里隐士不贴了。
SystemLoadDO.cs照常规写。
namespace DBPartitionTest
{
public class SystemLoadDO
{
#region Member Variables
protected DateTime _loggingDate;
protected float _cpuUsage;
protected float _memoryUsage;
protected int _bytesReceivedPerSecond;
protected int _bytesSentPerSecond;
#endregion
...
}
}
SystemLoadDO.hbm.xml照常规写,注意class节点里的table,隐士写了system_{0}_loads,看官说了,这个不能用呀,不要紧,反正后面会被NamingStrategy给替换掉。
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DBPartitionTest" assembly="DBPartitionTest">
<class name="SystemLoadDO" table="system_{0}_loads">
<id name="LoggingDate" type="DateTime" unsaved-value="2006-01-01 00:00:00">
<column name="loggingDate" sql-type="datetime" not-null="true" unique="true" index="PRIMARY"/>
<generator class="assigned" />
</id>
<property name="CpuUsage" type="float">
<column name="cpuUsage" sql-type="float" not-null="true" />
</property>
<property name="MemoryUsage" type="float">
<column name="memoryUsage" sql-type="float" not-null="true" />
</property>
<property name="BytesReceivedPerSecond" type="Int32">
<column name="bytesReceivedPerSecond" sql-type="int" not-null="false" />
</property>
<property name="BytesSentPerSecond" type="Int32">
<column name="bytesSentPerSecond" sql-type="int" not-null="false" />
</property>
</class>
</hibernate-mapping>
PartitionNamingStrategy.cs这个抽象类实现了接口INamingStrategy,它的构造函数接受一个数字索引,通过改写方法TableName来实现替换表名的工作,其他还是留给DefaultNamingStrategy。
namespace DBPartitionTest
{
public abstract class PartitionNamingStrategy : INamingStrategy
{
private int index;
public int Index
{
get { return index; }
}
private string partitionTableName;
public string PartitionTableName
{
get { return partitionTableName; }
}
public abstract string PartitionTableFormat { get;}
public PartitionNamingStrategy(int index)
{
this.index = index;
this.partitionTableName = string.Format(PartitionTableFormat, index); // 根据索引构造新的表名
}
#region INamingStrategy
public string ClassToTableName(string className)
{
return DefaultNamingStrategy.Instance.ClassToTableName(className);
}
public string PropertyToColumnName(string propertyName)
{
return DefaultNamingStrategy.Instance.PropertyToColumnName(propertyName);
}
public string TableName(string tableName)
{
if (PartitionTableFormat.Equals(tableName)) // 这句来实现表名替换
return PartitionTableName;
return DefaultNamingStrategy.Instance.TableName(tableName);
}
public string ColumnName(string columnName)
{
return DefaultNamingStrategy.Instance.ColumnName(columnName);
}
public string PropertyToTableName(string className, string propertyName)
{
return DefaultNamingStrategy.Instance.PropertyToTableName(className, propertyName);
}
#endregion
}
}
SystemLoadsNamingStrategy.cs是PartitionNamingStrategy的实现类,只需要实现属性PartitionTableFormat,注意这里必须返回和SystemLoadDO.hbm.xml里一样的串。实际项目里可以不必把这个串写死在代码里,Java这里就太简单了,直接Spring里配一个Bean就搞定了,隐士这里只是验证想法。
namespace DBPartitionTest
{
public class SystemLoadsNamingStrategy : PartitionNamingStrategy
{
public SystemLoadsNamingStrategy(int index)
: base(index)
{
}
public override string PartitionTableFormat
{
get { return "system_{0}_loads"; }
}
}
}
Program.cs是程序入口,这段代码生成了10个Configuration,10个Configuration创建了10个SessionFactory,每个SessionFactory互不干扰,自己认自己的分表操作,运行结果太长隐士就不附了。这里具体几张表也可以做在配置文件里,这样增加表、减少表可以做到不改代码。
namespace DBPartitionTest
{
public class Program
{
public static void Test()
{
for (int i = 1; i < 11; ++i)
{
Configuration configuration = new Configuration().SetNamingStrategy(new SystemLoadsNamingStrategy(i)).Configure();
ISessionFactory sessionFactory = configuration.BuildSessionFactory();
ISession session = null;
try
{
session = sessionFactory.OpenSession();
SystemLoadDO systemLoadDO = new SystemLoadDO();
systemLoadDO.LoggingDate = DateTime.Now;
systemLoadDO.CpuUsage = 80;
systemLoadDO.MemoryUsage = 70;
Console.WriteLine(systemLoadDO.LoggingDate.ToString());
session.Save(systemLoadDO);
session.Flush();
ICriteria criteria = session.CreateCriteria(typeof(SystemLoadDO));
criteria.AddOrder(Order.Desc("LoggingDate"));
criteria.SetFirstResult(0);
criteria.SetMaxResults(1);
systemLoadDO = criteria.UniqueResult<SystemLoadDO>();
Console.WriteLine(systemLoadDO.LoggingDate.ToString());
systemLoadDO.BytesReceivedPerSecond = 1024;
session.Flush();
session.Delete(systemLoadDO);
session.Flush();
}
catch (Exception e)
{
Console.WriteLine(e.InnerException);
Console.WriteLine(e.StackTrace);
Console.WriteLine(e.Message);
}
finally
{
if (session != null)
session.Close();
}
}
}
static void Main(string[] args)
{
Test();
}
}
}
总结一下,这个方法的优点是秉承了Hibernate的设计思路,没有修改Hibernate源码,而且是通过Hibernate所允许的方式来进行操作,可以说拿到SessionFactory后所有操作都是和不分表一样的,而且DO实例通过不同的SessionFactory保存、删除就可实现跨表复制、删除。由于SessionFactory不一样,所以缓存维护也没有影响。唯一的缺点就是要维护和分表数量一样的SessionFactory,貌似也就是多写几行代码而已。
隐士这里说完了,希望大家一起讨论。
分享到:
相关推荐
【hibernate动态分表】是一种数据库设计策略,主要用于处理大数据量的问题,通过将数据分散到多个物理表中,以实现水平扩展,提高查询效率,减轻单表的压力。在Java Web开发中,Hibernate作为一款流行的ORM(对象...
本文将深入探讨如何使用Hibernate作为ORM框架,结合Spring,实现一个自定义的分表插件。这个插件提供了一种灵活的策略定义方式,适用于各种业务场景。 标题中的"hibernate-分表插件实现思路"意味着我们要讨论的是...
Spring Hibernate 实现动态替换表名(分表)的方法 随着数据库的发展,分表操作变得越来越重要。今天,我们将讨论如何使用 Spring Hibernate 实现动态替换表名(分表)。 概述 ---- 在实现动态替换表名时,我们...
在实际操作中,分库分表的实现通常需要借助中间件或框架来简化开发和运维的难度。这些中间件能够屏蔽分库分表的复杂性,提供统一的SQL接口给上层应用使用,同时处理好数据分布、路由、事务、容错等复杂问题。常见的...
在本文中,我们将深入探讨如何使用ShardingSphere-JDBC来实现简单的单库分表策略,以便更好地管理和优化数据库性能。 首先,我们需要了解什么是数据分片。数据分片是将一个大型数据库拆分成多个较小的部分,每个...
在"MySQL 分库分表的实现原理及演示案例.pdf"这个文档中,你可能会看到如何在实际环境中应用这些概念和工具的详细步骤,包括设置分片规则、配置分库分表中间件、处理分布式事务等。通过学习和实践这些案例,你将能够...
本文将深入探讨如何利用SpringBoot的AOP(面向切面编程)特性,结合MyBatis的多数据源配置,实现动态表名的分库分表查询。 首先,我们需要理解SpringBoot的核心概念。SpringBoot是Spring框架的简化版本,它预设了...
.NET Core 实现分表分库、读写分离的通用 Repository 功能 .NET Core 实现分表分库、读写分离的通用 Repository 功能是指使用 FreeSql.Repository 库来实现通用的仓储层功能,实现了基础的仓储层(CURD),并且支持...
接下来我们将深入探讨标题和描述中涉及的“sharding-proxy实现分表”这一主题。 ### 1. 分库分表介绍 分库分表是数据库水平扩展的一种常见策略,用于解决单表数据量过大导致的性能问题。随着业务的增长,数据量...
通过研究这些代码,开发者可以学习如何在实际项目中实现数据库的分库分表,并利用Hibernate简化开发流程。 总之,"基于hibernate的mysql分表分库实例"是一个解决大数据量场景下的数据库管理问题的实践案例,它结合...
本项目将这两种技术结合,并利用策略设计模式和拦截器来实现一个按年份划分的数据库分表策略。下面将详细阐述这个项目中的核心知识点。 1. **SpringMVC**: SpringMVC是Spring框架的一部分,它是一个模型-视图-...
1. ruoyi框架基础集成了sharding5.0.0实现分表功能; 2. 采用动态数据源的方式,需要分表的连接采用单独的数据库连接; 3. 下载代码后,创建对应的数据库表,进行配置即可启动运行,测试功能; 4. 可以参考其中的...
本项目基于Java、SpringBoot、MyBatis以及ShardingJDBC实现了一个分库分表的解决方案,旨在帮助开发者理解并掌握这一技术。以下是关于这些技术的详细介绍: **Java**: Java是一种广泛使用的面向对象的编程语言,...
标题中的“mycat+mysql+jdbc实现根据手机号尾号分库分表存储”涉及的是分布式数据库中间件Mycat与MySQL数据库以及Java JDBC接口的结合使用。Mycat是一款开源的分布式数据库系统,用于解决大数据量、高并发的场景下的...
综上所述,"Python+MySQL分表分库实战"的学习内容涵盖了数据库设计原则、Python数据库操作技巧、分库分表策略以及性能优化等多个方面。通过学习和实践,开发者可以有效地解决大数据场景下的存储和查询问题,提高系统...
- 很简单,网上很多关于分表的都是含糊其辞,没有任何详细的,通用的,既然没有,那么我写一个出来吧。 - 主要目的 - 产品上线以后,数据量越来越大,当一个表有几十万上百万条记录的时候,是时候考虑分表了。...
本文将深入探讨如何使用MyBatis-Plus优雅地实现多数据源及分表策略,为系统的高效运行提供支持。 首先,多数据源是指在一个应用中同时连接并操作多个不同的数据库,这种设计模式常用于分布式系统或高可用架构中,以...
比如,INSERT、UPDATE和DELETE操作需要考虑到目标库和表的选择,可能需要拼接出多个操作语句。同时,JOIN操作变得复杂,可能需要在应用层进行数据聚合。 4. **事务处理**:由于操作可能分布在多个库上,事务的处理...
【大数据表的分表处理设计思想与实现】 在MySQL中,面对海量数据的存储和处理,单个表的规模过大可能会导致查询效率下降,甚至影响系统的稳定性和可用性。为了解决这一问题,分表设计成为了数据库优化的重要手段。...