ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

拜托,Hibernate主键生成策略sequence能不能别再用在String类型属性上

2019-12-03 18:03:34  阅读:153  来源: 互联网

标签:Hibernate String sequence hibernate sequenceName org import 主键


一、主键生成策略-sequence简介

  sequence又称为序列,适用于long、short或int类型的主键,Hibernate根据底层数据库序列生成标识符。只有支持序列的数据库才能使用该生成策略(如Oralce、DB2等,),MySQL数据库本身不支持序列,固无法使用到sequence生成策略。此处以Oracle数据库为例简单描述下Hibernate如何配置并使用序列作为主键。

  • Oracle数据库创建序列
create sequence TIGER_SEQ
minvalue 0
maxvalue 999999999999999999999999999
start with 10000
increment by 1
cache 20;
/
  • Hibernate映射文件(实体类类名.hbm.xml文件)配置序列
<id name="urid"> 
  <column name="urid"/>  
  <generator class="sequence"> 
    <param name="sequence">TIGER_SEQ</param> 
 </generator>
</id>

 

二、编写背景

  应客户方要求,在项目部署中间件要求使用weblogic12C,导致必须升级Spring框架到Spring4,相对应的Hibernate版本也由3.2.5.ga升级成3.6.10.Final。由于现有代码是祖传代码,之前设计Hibernate映射文件中使用了序列的主键对应的实体类却是String类型的,而升级后的版本只认long、shot和int类型,String类型的主键直接不支持了。直接改实体类对应的类型不现实,改动量太大而且容易改漏,只能硬着头皮去下载两个版本的Hibernate源码进行研究。

 

三、刨根问底

  根据下载的源码首先定位到处理主键生成策略的类在org\hibernate\id\下,按文件名含义很容易就找到了序列相关处理类SequenceGenerator.java(大牛写的框架命名还是挺规范的)。

  • 3.6.10.Final版本的SequenceGenerator.java文件源码
  1 import java.io.Serializable;
  2 import java.sql.PreparedStatement;
  3 import java.sql.ResultSet;
  4 import java.sql.SQLException;
  5 import java.util.Properties;
  6 
  7 import org.slf4j.Logger;
  8 import org.slf4j.LoggerFactory;
  9 import org.hibernate.HibernateException;
 10 import org.hibernate.MappingException;
 11 import org.hibernate.cfg.ObjectNameNormalizer;
 12 import org.hibernate.exception.JDBCExceptionHelper;
 13 import org.hibernate.dialect.Dialect;
 14 import org.hibernate.engine.SessionImplementor;
 15 import org.hibernate.mapping.Table;
 16 import org.hibernate.type.Type;
 17 import org.hibernate.util.PropertiesHelper;
 18 
 19 /**
 20  * <b>sequence</b><br>
 21  * <br>
 22  * Generates <tt>long</tt> values using an oracle-style sequence. A higher
 23  * performance algorithm is <tt>SequenceHiLoGenerator</tt>.<br>
 24  * <br>
 25  * Mapping parameters supported: sequence, parameters.
 26  *
 27  * @see SequenceHiLoGenerator
 28  * @see TableHiLoGenerator
 29  * @author Gavin King
 30  */
 31 public class SequenceGenerator implements PersistentIdentifierGenerator, Configurable {
 32     private static final Logger log = LoggerFactory.getLogger(SequenceGenerator.class);
 33 
 34     /**
 35      * The sequence parameter
 36      */
 37     public static final String SEQUENCE = "sequence";
 38 
 39     /**
 40      * The parameters parameter, appended to the create sequence DDL.
 41      * For example (Oracle): <tt>INCREMENT BY 1 START WITH 1 MAXVALUE 100 NOCACHE</tt>.
 42      */
 43     public static final String PARAMETERS = "parameters";
 44 
 45     private String sequenceName;
 46     private String parameters;
 47     private Type identifierType;
 48     private String sql;
 49 
 50     protected Type getIdentifierType() {
 51         return identifierType;
 52     }
 53 
 54     public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
 55         ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER );
 56         sequenceName = normalizer.normalizeIdentifierQuoting(
 57                 PropertiesHelper.getString( SEQUENCE, params, "hibernate_sequence" )
 58         );
 59         parameters = params.getProperty( PARAMETERS );
 60 
 61         if ( sequenceName.indexOf( '.' ) < 0 ) {
 62             final String schemaName = normalizer.normalizeIdentifierQuoting( params.getProperty( SCHEMA ) );
 63             final String catalogName = normalizer.normalizeIdentifierQuoting( params.getProperty( CATALOG ) );
 64             sequenceName = Table.qualify(
 65                     dialect.quote( catalogName ),
 66                     dialect.quote( schemaName ),
 67                     dialect.quote( sequenceName )
 68             );
 69         }
 70         else {
 71             // if already qualified there is not much we can do in a portable manner so we pass it
 72             // through and assume the user has set up the name correctly.
 73         }
 74 
 75         this.identifierType = type;
 76         sql = dialect.getSequenceNextValString( sequenceName );
 77     }
 78 
 79     public Serializable generate(SessionImplementor session, Object obj) {
 80         return generateHolder( session ).makeValue();
 81     }
 82 
 83     protected IntegralDataTypeHolder generateHolder(SessionImplementor session) {
 84         try {
 85             PreparedStatement st = session.getBatcher().prepareSelectStatement( sql );
 86             try {
 87                 ResultSet rs = st.executeQuery();
 88                 try {
 89                     rs.next();
 90                     IntegralDataTypeHolder result = buildHolder();
 91                     result.initialize( rs, 1 );
 92                     if ( log.isDebugEnabled() ) {
 93                         log.debug("Sequence identifier generated: " + result);
 94                     }
 95                     return result;
 96                 }
 97                 finally {
 98                     rs.close();
 99                 }
100             }
101             finally {
102                 session.getBatcher().closeStatement(st);
103             }
104 
105         }
106         catch (SQLException sqle) {
107             throw JDBCExceptionHelper.convert(
108                     session.getFactory().getSQLExceptionConverter(),
109                     sqle,
110                     "could not get next sequence value",
111                     sql
112             );
113         }
114     }
115 
116     protected IntegralDataTypeHolder buildHolder() {
117         return IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() );
118     }
119 
120     public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
121         String[] ddl = dialect.getCreateSequenceStrings(sequenceName);
122         if ( parameters != null ) {
123             ddl[ddl.length - 1] += ' ' + parameters;
124         }
125         return ddl;
126     }
127 
128     public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
129         return dialect.getDropSequenceStrings(sequenceName);
130     }
131 
132     public Object generatorKey() {
133         return sequenceName;
134     }
135 
136     public String getSequenceName() {
137         return sequenceName;
138     }
139 
140 }
  • 3.2.5.ga版本的SequenceGenerator.java文件源码
  1 //$Id: SequenceGenerator.java 9686 2006-03-27 16:47:06Z steve.ebersole@jboss.com $
  2 package org.hibernate.id;
  3 
  4 import java.io.Serializable;
  5 import java.sql.PreparedStatement;
  6 import java.sql.ResultSet;
  7 import java.sql.SQLException;
  8 import java.util.Properties;
  9 
 10 import org.apache.commons.logging.Log;
 11 import org.apache.commons.logging.LogFactory;
 12 import org.hibernate.HibernateException;
 13 import org.hibernate.MappingException;
 14 import org.hibernate.exception.JDBCExceptionHelper;
 15 import org.hibernate.dialect.Dialect;
 16 import org.hibernate.engine.SessionImplementor;
 17 import org.hibernate.mapping.Table;
 18 import org.hibernate.type.Type;
 19 import org.hibernate.util.PropertiesHelper;
 20 
 21 /**
 22  * <b>sequence</b><br>
 23  * <br>
 24  * Generates <tt>long</tt> values using an oracle-style sequence. A higher
 25  * performance algorithm is <tt>SequenceHiLoGenerator</tt>.<br>
 26  * <br>
 27  * Mapping parameters supported: sequence, parameters.
 28  *
 29  * @see SequenceHiLoGenerator
 30  * @see TableHiLoGenerator
 31  * @author Gavin King
 32  */
 33 
 34 public class SequenceGenerator implements PersistentIdentifierGenerator, Configurable {
 35 
 36     /**
 37      * The sequence parameter
 38      */
 39     public static final String SEQUENCE = "sequence";
 40 
 41     /**
 42      * The parameters parameter, appended to the create sequence DDL.
 43      * For example (Oracle): <tt>INCREMENT BY 1 START WITH 1 MAXVALUE 100 NOCACHE</tt>.
 44      */
 45     public static final String PARAMETERS = "parameters";
 46 
 47     private String sequenceName;
 48     private String parameters;
 49     private Type identifierType;
 50     private String sql;
 51 
 52     private static final Log log = LogFactory.getLog(SequenceGenerator.class);
 53 
 54     public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
 55         sequenceName = PropertiesHelper.getString(SEQUENCE, params, "hibernate_sequence");
 56         parameters = params.getProperty(PARAMETERS);
 57         String schemaName = params.getProperty(SCHEMA);
 58         String catalogName = params.getProperty(CATALOG);
 59 
 60         if (sequenceName.indexOf( '.' ) < 0) {
 61             sequenceName = Table.qualify( catalogName, schemaName, sequenceName );
 62         }
 63 
 64         this.identifierType = type;
 65         sql = dialect.getSequenceNextValString(sequenceName);
 66     }
 67 
 68     public Serializable generate(SessionImplementor session, Object obj) 
 69     throws HibernateException {
 70         
 71         try {
 72 
 73             PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
 74             try {
 75                 ResultSet rs = st.executeQuery();
 76                 try {
 77                     rs.next();
 78                     Serializable result = IdentifierGeneratorFactory.get(
 79                             rs, identifierType
 80                         );
 81                     if ( log.isDebugEnabled() ) {
 82                         log.debug("Sequence identifier generated: " + result);
 83                     }
 84                     return result;
 85                 }
 86                 finally {
 87                     rs.close();
 88                 }
 89             }
 90             finally {
 91                 session.getBatcher().closeStatement(st);
 92             }
 93             
 94         }
 95         catch (SQLException sqle) {
 96             throw JDBCExceptionHelper.convert(
 97                     session.getFactory().getSQLExceptionConverter(),
 98                     sqle,
 99                     "could not get next sequence value",
100                     sql
101                 );
102         }
103 
104     }
105 
106     public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
107         String[] ddl = dialect.getCreateSequenceStrings(sequenceName);
108         if ( parameters != null ) {
109             ddl[ddl.length - 1] += ' ' + parameters;
110         }
111         return ddl;
112     }
113 
114     public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
115         return dialect.getDropSequenceStrings(sequenceName);
116     }
117 
118     public Object generatorKey() {
119         return sequenceName;
120     }
121 
122     public String getSequenceName() {
123         return sequenceName;
124     }
125 
126 }

  上面两个源码文件比对下发现,3.6.10.Final版本调用的是buildHolder()方法中的getIntegralDataTypeHolder方法进行处理,而getIntegralDataTypeHolder方法是直接就把序列对应的属性类型当成了long、shot和int类型(或对应包装类),这是导致将序列用在String类型属性上出错的根本原因。

 

三、对症下药

  经过源码分析发现,升级到3.6.10.Final版本后只需要改动SequenceGenerator类中的generate方法,进行兼容性处理即可,具体改造点如下(红色加粗):

  1 /*
  2  * Hibernate, Relational Persistence for Idiomatic Java
  3  *
  4  * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
  5  * indicated by the @author tags or express copyright attribution
  6  * statements applied by the authors.  All third-party contributions are
  7  * distributed under license by Red Hat Inc.
  8  *
  9  * This copyrighted material is made available to anyone wishing to use, modify,
 10  * copy, or redistribute it subject to the terms and conditions of the GNU
 11  * Lesser General Public License, as published by the Free Software Foundation.
 12  *
 13  * This program is distributed in the hope that it will be useful,
 14  * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 15  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 16  * for more details.
 17  *
 18  * You should have received a copy of the GNU Lesser General Public License
 19  * along with this distribution; if not, write to:
 20  * Free Software Foundation, Inc.
 21  * 51 Franklin Street, Fifth Floor
 22  * Boston, MA  02110-1301  USA
 23  */
 24 package org.hibernate.id;
 25 
 26 import java.io.Serializable;
 27 import java.sql.PreparedStatement;
 28 import java.sql.ResultSet;
 29 import java.sql.SQLException;
 30 import java.util.Properties;
 31 
 32 import org.hibernate.id.factory.IdentifierGeneratorFactory;
 33 import org.slf4j.Logger;
 34 import org.slf4j.LoggerFactory;
 35 import org.hibernate.HibernateException;
 36 import org.hibernate.MappingException;
 37 import org.hibernate.cfg.ObjectNameNormalizer;
 38 import org.hibernate.exception.JDBCExceptionHelper;
 39 import org.hibernate.dialect.Dialect;
 40 import org.hibernate.engine.SessionImplementor;
 41 import org.hibernate.mapping.Table;
 42 import org.hibernate.type.Type;
 43 import org.hibernate.util.PropertiesHelper;
 44 
 45 /**
 46  * <b>sequence</b><br>
 47  * <br>
 48  * Generates <tt>long</tt> values using an oracle-style sequence. A higher
 49  * performance algorithm is <tt>SequenceHiLoGenerator</tt>.<br>
 50  * <br>
 51  * Mapping parameters supported: sequence, parameters.
 52  *
 53  * @see SequenceHiLoGenerator
 54  * @see TableHiLoGenerator
 55  * @author Gavin King
 56  */
 57 public class SequenceGenerator implements PersistentIdentifierGenerator, Configurable {
 58     private static final Logger log = LoggerFactory.getLogger(SequenceGenerator.class);
 59 
 60     /**
 61      * The sequence parameter
 62      */
 63     public static final String SEQUENCE = "sequence";
 64 
 65     /**
 66      * The parameters parameter, appended to the create sequence DDL.
 67      * For example (Oracle): <tt>INCREMENT BY 1 START WITH 1 MAXVALUE 100 NOCACHE</tt>.
 68      */
 69     public static final String PARAMETERS = "parameters";
 70 
 71     private String sequenceName;
 72     private String parameters;
 73     private Type identifierType;
 74     private String sql;
 75 
 76     protected Type getIdentifierType() {
 77         return identifierType;
 78     }
 79 
 80     public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
 81         ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER );
 82         sequenceName = normalizer.normalizeIdentifierQuoting(
 83                 PropertiesHelper.getString( SEQUENCE, params, "hibernate_sequence" )
 84         );
 85         parameters = params.getProperty( PARAMETERS );
 86 
 87         if ( sequenceName.indexOf( '.' ) < 0 ) {
 88             final String schemaName = normalizer.normalizeIdentifierQuoting( params.getProperty( SCHEMA ) );
 89             final String catalogName = normalizer.normalizeIdentifierQuoting( params.getProperty( CATALOG ) );
 90             sequenceName = Table.qualify(
 91                     dialect.quote( catalogName ),
 92                     dialect.quote( schemaName ),
 93                     dialect.quote( sequenceName )
 94             );
 95         }
 96         else {
 97             // if already qualified there is not much we can do in a portable manner so we pass it
 98             // through and assume the user has set up the name correctly.
 99         }
100 
101         this.identifierType = type;
102         sql = dialect.getSequenceNextValString( sequenceName );
103     }
104 
105     public Serializable generate(SessionImplementor session, Object obj) {
106         if(identifierType.getReturnedClass()==String.class){
107             try {
108 
109                 PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
110                 try {
111                     ResultSet rs = st.executeQuery();
112                     try {
113                         rs.next();
114                         Serializable result =rs.getString( 1 );
115                         if ( log.isDebugEnabled() ) {
116                             log.debug("Sequence identifier generated: " + result);
117                         }
118                         return result;
119                     }
120                     finally {
121                         rs.close();
122                     }
123                 }
124                 finally {
125                     session.getBatcher().closeStatement(st);
126                 }
127 
128             }
129             catch (SQLException sqle) {
130                 throw JDBCExceptionHelper.convert(
131                         session.getFactory().getSQLExceptionConverter(),
132                         sqle,
133                         "could not get next sequence value",
134                         sql
135                 );
136             }
137         }else{
138             return generateHolder( session ).makeValue();
139         }
140     }
141 
142     protected IntegralDataTypeHolder generateHolder(SessionImplementor session) {
143         try {
144             PreparedStatement st = session.getBatcher().prepareSelectStatement( sql );
145             try {
146                 ResultSet rs = st.executeQuery();
147                 try {
148                     rs.next();
149                     IntegralDataTypeHolder result = buildHolder();
150                     result.initialize( rs, 1 );
151                     if ( log.isDebugEnabled() ) {
152                         log.debug("Sequence identifier generated: " + result);
153                     }
154                     return result;
155                 }
156                 finally {
157                     rs.close();
158                 }
159             }
160             finally {
161                 session.getBatcher().closeStatement(st);
162             }
163 
164         }
165         catch (SQLException sqle) {
166             throw JDBCExceptionHelper.convert(
167                     session.getFactory().getSQLExceptionConverter(),
168                     sqle,
169                     "could not get next sequence value",
170                     sql
171             );
172         }
173     }
174 
175     protected IntegralDataTypeHolder buildHolder() {
176         return IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() );
177     }
178 
179     public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
180         String[] ddl = dialect.getCreateSequenceStrings(sequenceName);
181         if ( parameters != null ) {
182             ddl[ddl.length - 1] += ' ' + parameters;
183         }
184         return ddl;
185     }
186 
187     public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
188         return dialect.getDropSequenceStrings(sequenceName);
189     }
190 
191     public Object generatorKey() {
192         return sequenceName;
193     }
194 
195     public String getSequenceName() {
196         return sequenceName;
197     }
198 
199 }

 

四、小结

  此次并未对Hibernate源码进行深入剖析,只是业务实际场景上碰到了就想办法解决。当遇到框架受限时别急着放弃,重写源码或许是一条很好的出路。


标签:Hibernate,String,sequence,hibernate,sequenceName,org,import,主键
来源: https://www.cnblogs.com/tiger-king/p/11978428.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有