hibernate动态表名映射

  1. 在实际的项目应用中,有时会设计出这样的一种数据表,每个时间段产生一个新表,例如是按年或月或日。相同类型的表中,所有的字段结构都是一样的。而 hibernate 提供的类与表的映射,是只能映射到一个具体表的,在程序的运行过程中,很难去动态修改一个 hbm 对应的表名。我在网上也有看到一实现,但是很复杂,并且不符合我的要求。  
  2.   
  3. 因此我就想到直接用 jdbc 去操作数据库,这样的做法是绕过 hibernate 了。方法是从 hibernate 的 session 中,直接取得数据库 connection ,然后就直接 jdbc 了。  
  4. 后来在升级了 proxool 到 9.0RC3 后,发现居然出现了数据库连接无法释放的问题。为了解决这个问题,我查阅了 hibernate doc。我发现原来用 SQLQuery 可以更好的解决,并且可以重新用于 hibernate hbm 机制。以下举例说明。  
  5. 例如我有一个 pojo 是 ReadInfo,用来记录阅读信息的。由于数据量宠大,所以我的思路是按月划分,每个月一张表。所以只是表名不同,而字段是完全相同的。  
  6.   
  7. ReadInfo.java 是这样的,其中 userId, year, month, day 是联合主键:  
  8.   
  9.     private Integer userId;      
  10.     private Integer year;      
  11.     private Integer month;      
  12.     private Integer day;      
  13.     private Integer point;  
  14.    
  15.   
  16.   
  17. 那么相应的 ReadInfo.hbm.xml 的片段是  
  18.   
  19.   
  20.     <class name="ReadInfo" table="tblReadInfo" mutable="false">  
  21.         <composite-id>  
  22.             <key-property name="userId" column="userId" type="integer"/>  
  23.             <key-property name="year" column="year" type="integer"/>  
  24.             <key-property name="month" column="month" type="integer"/>  
  25.             <key-property name="day" column="day" type="integer"/>  
  26.         </composite-id>  
  27.         <property name="point" column="point" type="integer"/>  
  28.     </class>  
  29.    
  30.    
  31.   
  32.   
  33. 上面的xml,注意 个细节  
  34.   
  35. 1. pojo 所映射的 table tblReadInfo 实际上是不存在的。实际的表是 tblRead200710 之类的;  
  36.   
  37. 2. mutable 要设置为 false,即是说,关闭 hibernate 对这个 pojo 的任何持久化操作,以避免 hibernate 把数据写到 tblReadInfo 中(这个表是不存在的嘛)。因此,所有的持久化操作,都是需要自己通过 SQLQuery 来处理。  
  38.   
  39. 现在可以看一下 ado 中的操作了,先看一个 select 操作  
  40. public ReadInfo selectReadInfo(Integer userId, Integer year,  
  41.             Integer month, Integer day) throws HibernateException  
  42.      
  43.          ReadInfo readInfo null;  
  44.          Session session getSession();  
  45.         Transaction tx session.beginTransaction();  
  46.         try  
  47.          
  48.             String sql "select from tblRead"  
  49.                 Misc.formatMoon(year, month)  
  50.                 where userId=? and day=?";  
  51.             SQLQuery query session.createSQLQuery(sql);  
  52.             query.addEntity(ReadInfo.class);  
  53.             query.setLong(0, userId);  
  54.             query.setInteger(1, day);  
  55.              readInfo (ReadInfo) query.uniqueResult();  
  56.              tx.commit();  
  57.          
  58.         catch (HibernateException e)  
  59.          
  60.             log.error("catch exception:", e);  
  61.             if (tx != null)  
  62.              
  63.                  tx.rollback();  
  64.              
  65.             throw e;  
  66.          
  67.         return readInfo;  
  68.      
  69.   
  70.   
  71.    
  72.   
  73.   
  74. 上面的代码,关键是以下几点:  
  75.   
  76. 1. 通过函数参数的 year, month 来确定要操作的表名,我自己写了一个 Misc.formatMoon(year, month) 来生成 "yyyyMM" 格式的字串;  
  77.   
  78. 2. 使用了 SQLQuery ,再通过 query.addEntity(ReadInfo.class); 建立与 ReadInfo 的映射关系;  
  79.   
  80. 3. query.setXxx() 与 PreparedStatement 的类似,不过索引是从 开始;  
  81.   
  82. 4. 其它的就跟一般的 Query 操作类似的了。  
  83.   
  84. 再看一个 insert 操作  
  85.   
  86.     public void insertReadInfo(ReadInfo readInfo) throws HibernateException  
  87.      
  88.          Session session getSession();  
  89.         Transaction tx session.beginTransaction();  
  90.         try  
  91.          
  92.             String sql "insert into tblRead"  
  93.                 Misc.formatMoon(readInfo.getYear(), readInfo.getMonth())  
  94.                 (userId, year, month, day, point) values (?, ?, ?, ?, ?)";   
  95.             SQLQuery query session.createSQLQuery(sql);  
  96.             query.setLong(0, readInfo.getUserId());  
  97.             query.setInteger(1, readInfo.getYear());  
  98.             query.setInteger(2, readInfo.getMonth());  
  99.             query.setInteger(3, readInfo.getDay());  
  100.             query.setInteger(4, readInfo.getPoint());  
  101.             query.executeUpdate();  
  102.              tx.commit();  
  103.          
  104.         catch (HibernateException e)  
  105.          
  106.             log.error("catch exception:", e);  
  107.             if (tx != null)  
  108.              
  109.                  tx.rollback();  
  110.              
  111.             throw e;  
  112.          
  113.      
  114.   
  115.   
  116.   
  117. 同理,update, delete 等操作也是这样实现的。  
  118.   
  119. hmm.. 这种处理方式的麻烦的地方是需要手工写 sql ,因此要尽量写通用的标准 sql,不然在数据库兼容方面会有问题。当然,有时是会出现无法兼容的情况,那么可以考虑把 sql 写到配置文件中,根据不同的数据库,装载相应的配置文件咯。
### Mysql 中表映射及相关配置 #### 逻辑表与物理表的概念 在数据库设计中,通常存在两种类型的称:逻辑称和物理称。逻辑称是指开发人员或应用程序内部使用的抽象字,而物理称则是指存储于数据库中的真实对象的字。对于 MySQL 来说,默认情况下它不会自动执行任何特殊的转换过程来改变从 JPA 或其他 ORM 工具传递过来的实体类属性或者方法对应到数据库里的字段或是表格的实际称呼形式[^1]。 然而,在实际项目当中往往会有特定的企业标准或者是技术架构方面的要求使得我们需要遵循一定的命约定比如统一采用小写字母加下划线分割单词的形式作为最终落地保存下来的数据结构标识符样式而不是单纯依靠程序代码里定义好的原始变量表达方式呈现出来给底层持久化层认识理解并加以运用处理。这就涉及到所谓的 **Physical Naming Strategy**(物理命策略)[^1]。 #### 配置 JPA 实现自定义映射规则 当使用 Java Persistence API(JPA) 进行数据访问时, 如果发现由于大小写敏感度差异造成找不到相应的目标资源错误现象发生的话,则可以通过调整 Hibernate 提供的一系列相关参数解决此类矛盾冲突问题: - 设置 hibernate.physical_naming_strategy 属性指定具体的实现类完成定制化的转化功能; ```properties spring.jpa.properties.hibernate.physical_naming_strategy=com.example.CustomNamingStrategy ``` 其中 CustomNamingStrategy 可继承 org.springframework.boot.orm.jpa.SpringPhysicalNamingStrategy 并重载 necessary methods 定义自己的算法逻辑满足特殊场景下的需求[^2]。 另外一种更为简便快捷的方式就是在 application.yml 文件内直接声明好固定的模式即可生效无需额外编码工作量: ```yaml spring: jpa: database-platform: org.hibernate.dialect.MySQLDialect open-in-view: false properties: hibernate: naming: physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl ``` 以上例子表明我们选择了 Standard Implementation 方式来进行基本一致性的适配操作从而减少不必要的麻烦困扰提升整体效率效果明显优于手动逐一手动修正每一个地方带来的不便之处[^2]。 #### 数据库分片环境下的进一步考虑因素 随着业务规模不断扩大单一实例难以承载全部负荷压力于是引入了 Sharding 技术即把原本集中式的大型关系型管理系统拆解成多个相对独立又相互协作的小单元共同承担起原来的整体职责职能作用发挥各自优势最大化利用现有硬件设施条件达到降低成本提高性能的目的成果显著令人满意欣慰不已啊哈哈哈哈哈!!! 在这种复杂的分布式体系结构之下除了常规意义上的简单替换之外还需要更加深入细致周密全面综合考量各种可能性制定详尽完善的规划方案确保各个环节紧密衔接无缝对接不出差错失误才行哦亲~比如说前面提到过的那个所谓“规则”的概念就显得尤为重要起来了因为它直接影响到最后能否准确无误地定位目标位置找到确切答案解决问题啦!所以一定要高度重视起来哟各位小伙伴们记住咯😊💖✨🎉👏🎊🎈🎁🏆🏅🌟💫💥🔥🚀🌌🌍🌞🌈🌊💨🎶🎵🎧🎤📢🔔🔕⏰⏳⏱️📅📆📌📍📝📋💼📁📂🗂️📅🗓️📌📍🎯👋👋🏻👋🏽👋🏾👋🏿👍👎👌🙌🙏🤝👏💪✌️🤞🫠🫡👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋🏾👋🏿👋👋🏻👋🏽👋を超え👋
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值