`

【飞天奔月出品】聊聊JPA之GenerationType.AUTO

    博客分类:
  • jpa
JPA 
阅读更多

多说 GenerationType.AUTO 适用于多个数据库,

 

写道
在我们的应用中,一般选用@GeneratedValue(strategy=GenerationType.AUTO)这种方式,自动选择主键生成策略,以适应不同的数据库移植。

 

为什么我今天玩 sqlserver 可以创建主键, 但是主键却没有 IDENTITY标识呢?

 

难道是老人说的是错的? 难道教科书上写得不对? 事出反常必有妖,我偏偏不信这个邪

 

1.现象

 

现在这么一个简单的JPA类

 

 

@Entity
@Table(name = "T_SYS_CHOOSE_OPTION")
public class ChooseOption extends BaseModel implements Command{

    private Long id;

    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Long getId(){
        return id;
    }

    public void setId(Long id){
        this.id = id;
    }
}

 

 

但是启动程序,  生成的表,id列却没有 IDENTITY 标识



 

2.调试

 

将 logback 相关类日志调整成 debug

 

 

<logger name="org.hibernate.tool.hbm2ddl.SchemaUpdate" level="DEBUG" />

 

 

对比下  GenerationType.AUTO 与 GenerationType.IDENTITY 生成的SQL语句

 

--GenerationType.IDENTITY

create table T_SYS_CHOOSE_OPTION (ID numeric(19,0) identity not null)

 

--GenerationType.AUTO

create table T_SYS_CHOOSE_OPTION (ID numeric(19,0) not null)

 

 很清晰的发现,生成的SQL不同点

 

 

3.分析问题

 

上面的想象,搜索了 google ,stackoverflow 等网站, 有用的文章不是很多

 

那我只有自己动手了,  拿出绝招, 那就是 源代码跟踪

 

最终定位到 org.hibernate.mapping.Table.sqlCreateString(Dialect, Mapping, String, String) 方法

 

核心代码片段:

 

 

public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog, String defaultSchema) {
		StringBuffer buf = new StringBuffer( hasPrimaryKey() ? dialect.getCreateTableString() : dialect.getCreateMultisetTableString() )
				.append( ' ' )
				.append( getQualifiedName( dialect, defaultCatalog, defaultSchema ) )
				.append( " (" );

		boolean identityColumn = idValue != null && idValue.isIdentityColumn( p.getIdentifierGeneratorFactory(), dialect );

		// Try to find out the name of the primary key to create it as identity if the IdentityGenerator is used
		String pkname = null;
		if ( hasPrimaryKey() && identityColumn ) {
			pkname = ( (Column) getPrimaryKey().getColumnIterator().next() ).getQuotedName( dialect );
		}

		Iterator iter = getColumnIterator();
		while ( iter.hasNext() ) {
			Column col = (Column) iter.next();

			buf.append( col.getQuotedName( dialect ) )
					.append( ' ' );

			if ( identityColumn && col.getQuotedName( dialect ).equals( pkname ) ) {
				// to support dialects that have their own identity data type
				if ( dialect.hasDataTypeInIdentityColumn() ) {
					buf.append( col.getSqlType( dialect, p ) );
				}
				buf.append( ' ' )
						.append( dialect.getIdentityColumnString( col.getSqlTypeCode( p ) ) );
			}
			else {

.....

 

 

生成 Identity 的关键语句是 

 

 

buf.append( ' ' )
						.append( dialect.getIdentityColumnString( col.getSqlTypeCode( p ) ) );

 

 

 

我这里 dialect 配置的是 

 

 

hibernate.dialect=org.hibernate.dialect.SQLServerDialect

 

 

该方言  getIdentityColumnString 方法的实现 如下:



 

 

可以看出  GenerationType.IDENTITY 的实现原理是拼接了 identity not null

 

那为什么  GenerationType.AUTO 就没有拼接呢? 难道是我的人品不好?

 

别急,还有个关键代码 是  上面的 if 流程判断

 

 

if ( identityColumn && col.getQuotedName( dialect ).equals( pkname ) ) {

 

 

在于  

boolean identityColumn = idValue != null && idValue.isIdentityColumn( p.getIdentifierGeneratorFactory(), dialect );

 

 

而我当 GenerationType.AUTO , 调试到这个地方的时候,   identityColumn =false 

 

进一步跟踪 发现 

 

org.hibernate.mapping.SimpleValue.isIdentityColumn(IdentifierGeneratorFactory, Dialect)

 

 

	public boolean isIdentityColumn(IdentifierGeneratorFactory identifierGeneratorFactory, Dialect dialect) {
		identifierGeneratorFactory.setDialect( dialect );
		return identifierGeneratorFactory.getIdentifierGeneratorClass( identifierGeneratorStrategy )
				.equals( IdentityGenerator.class );
	}

 

 

其中的  identifierGeneratorStrategy 值为   org.hibernate.id.enhanced.SequenceStyleGenerator

 

 

导致  下面的代码,并没有使用  native 去取到  dialect.getNativeIdentifierGeneratorClass()

 

 

 

	/**
	 * {@inheritDoc}
	 */
	public Class getIdentifierGeneratorClass(String strategy) {
		if ( "native".equals( strategy ) ) {
			return dialect.getNativeIdentifierGeneratorClass();
		}

		Class generatorClass = ( Class ) generatorStrategyToClassNameMap.get( strategy );
		try {
			if ( generatorClass == null ) {
				generatorClass = ReflectHelper.classForName( strategy );
			}
		}
		catch ( ClassNotFoundException e ) {
			throw new MappingException( "Could not interpret id generator strategy [" + strategy + "]" );
		}
		return generatorClass;
	}

 

 

 

4. 为毛 identifierGeneratorStrategy 值为   org.hibernate.id.enhanced.SequenceStyleGenerator

 

追本溯源 , 我们来到了 generatorType 定义的地方

 

org.hibernate.cfg.AnnotationBinder.processId(PropertyHolder, PropertyData, SimpleValue, HashMap<String, IdGenerator>, boolean, ExtendedMappings)

 

 

	private static void processId(
			PropertyHolder propertyHolder,
			PropertyData inferredData,
			SimpleValue idValue,
			HashMap<String, IdGenerator> classGenerators,
			boolean isIdentifierMapper,
			ExtendedMappings mappings) {
.........


		String generatorType = generatedValue != null ?
				generatorType( generatedValue.strategy(), mappings ) :
				"assigned";
		String generatorName = generatedValue != null ?
				generatedValue.generator() :
				BinderHelper.ANNOTATION_STRING_DEFAULT;
		if ( isComponent ) {
			generatorType = "assigned";
		} //a component must not have any generator
		BinderHelper.makeIdGenerator( idValue, generatorType, generatorName, mappings, localGenerators );

.....

 

 

以及 org.hibernate.cfg.AnnotationBinder.generatorType(GenerationType, ExtendedMappings)

 

 

	private static String generatorType(GenerationType generatorEnum, ExtendedMappings mappings) {
		boolean useNewGeneratorMappings = mappings.useNewGeneratorMappings();
		switch ( generatorEnum ) {
			case IDENTITY:
				return "identity";
			case AUTO:
				return useNewGeneratorMappings
						? org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName()
						: "native";
			case TABLE:
				return useNewGeneratorMappings
						? org.hibernate.id.enhanced.TableGenerator.class.getName()
						: MultipleHiLoPerTableGenerator.class.getName();
			case SEQUENCE:
				return useNewGeneratorMappings
						? org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName()
						: "seqhilo";
		}
		throw new AssertionFailure( "Unknown GeneratorType: " + generatorEnum );
	}

 

 

 

哦,  原来在这里 

 

			case AUTO:
				return useNewGeneratorMappings
						? org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName()
						: "native";

 

 

发现, 当 AUTO 的时候,  如果  useNewGeneratorMappings为true的话 ,那么 就会 使用 SequenceStyleGenerator 而不是 传说中的  native

 

 

那么 这里的  useNewGeneratorMappings 是否可以变更呢?

看下 useNewGeneratorMappings 的 源码  org.hibernate.cfg.AnnotationConfiguration.ExtendedMappingsImpl.useNewGeneratorMappings()

 

 

 

		public boolean useNewGeneratorMappings() {
			if ( useNewGeneratorMappings == null ) {
				final String booleanName = getConfigurationProperties().getProperty( USE_NEW_ID_GENERATOR_MAPPINGS );
				useNewGeneratorMappings = Boolean.valueOf( booleanName );
			}
			return useNewGeneratorMappings.booleanValue();
		}

 

 

hoho,原来读取的是 配置文件中  hibernate.id.new_generator_mappings 的值

刚好我这里的该参数的值是true

 

 

hibernate.id.new_generator_mappings=true

原来如此, 仅需把该参数的值设为false 即可解决问题

 

 

 

 

5. hibernate.id.new_generator_mappings 是什么鬼?

知其然而知其所以然,是吾辈的目标

 

 

先来看看 hibernate 对这个参数的定义javadoc

 

 

	/**
	 * Setting which indicates whether or not the new {@link org.hibernate.id.IdentifierGenerator} are used
	 * for AUTO, TABLE and SEQUENCE.
	 * Default to false to keep backward compatibility.
	 */
	public static final String USE_NEW_ID_GENERATOR_MAPPINGS = "hibernate.id.new_generator_mappings";

 

 

 

在 http://stackoverflow.com/questions/25047226/jpa-generationtype-auto-not-considering-column-with-auto-increment#25052275 这个文章里面, 我们可以发现点有价值的内容

 

写道
If you use the new identifier generators:

properties.put("hibernate.id.new_generator_mappings", "true");

The AUTO will actually use a SequenceStyleGenerator and where the database doesn't support sequences, you end up using a TABLE generator instead (which is a portable solution but it's less efficient than IDENTITY or SEQUENCE).

 

 

我们再来看看  SequenceStyleGenerator 

 

 

Generator Description
   
SequenceStyleGenerator It’s an enhanced version of the previous sequence generator. It uses a sequence if the underlying database supports them. If the current database doesn’t support sequences it switches to using a table for generating sequence values. While the previous generators were having a predefined optimization algorithm, the enhanced generators can be configured with an optimizer strategy:

 

  • none: there is no optimizing strategy applied, so every identifier is fetched from the database
  • hi/lo: it uses the original hi/lo algorithm. This strategy makes it difficult for other systems to share the same identifier sequence, requiring other systems to implement the same identifier generation logic.
  • pooled: This optimizer uses a hi/lo optimization strategy, but instead of saving the current hi value it stores the current range upper boundary (or lower boundary –hibernate.id.optimizer.pooled.prefer_lo).

Pooled is the default optimizer strategy.

   

大意是,  SequenceStyleGenerator  是个增强的  sequence 生成器 ,如果底层数据库支持 序列,那么使用 序列, 如果不支持, 那么切换成 table来生成序列 values.

 

这也是,我在sqlserver 中,发现 了一个 hibernate_sequence 莫名其妙的表的原因

 




 
 

 

6. 总结

 

如果是 sqlserver 数据库, 使用GenerationType.AUTO ,不要配置  hibernate.id.new_generator_mappings=true

 

 

 

  • 大小: 13.9 KB
  • 大小: 25.8 KB
  • 大小: 25.8 KB
  • 大小: 8.1 KB
分享到:
评论

相关推荐

    【飞天奔月出品】windows版nginx 快速操控神器(启动start,关闭stop,重启restart) 批处理

    标题中的“【飞天奔月出品】windows版nginx 快速操控神器(启动start,关闭stop,重启restart) 批处理”指的是一个专为Windows操作系统设计的Nginx管理工具,它通过批处理脚本实现了Nginx服务的便捷启动、停止和重启...

    HTML5兔子奔月吃月饼微信小游戏代码.zip

    在这款“兔子奔月吃月饼”的游戏中,我们可以推测玩家需要控制兔子角色在月球表面跳跃,收集月饼以获得分数。游戏可能包含计时机制、等级系统或者难度递增的障碍物,以增加挑战性和趣味性。同时,由于是微信小游戏,...

    HTML5兔子奔月吃月饼微信小游戏代码.7z

    "兔子奔月"这个主题很可能使用了CSS3来完成动画效果,比如兔子跳跃的动画,月亮升起的过程等。CSS3的过渡(transition)和动画(animation)属性可以轻松创建平滑的视觉效果,增强游戏的吸引力。 此外,"吃月饼"这...

    鲁迅《奔月》作品分析.pdf

    《奔月》是鲁迅先生的一篇短篇小说,收录于其《故事新编》之中,通过对传统神话的再创作,鲁迅以戏拟的手法揭示了深刻的社会与人性问题。这篇作品通过对后羿这一昔日英雄形象的塑造,反映出鲁迅对时代变迁下英雄命运...

    Photoshop合成奔月女孩梦幻艺术照片效果.doc

    这篇文档介绍了如何使用Adobe Photoshop软件创作一张奔月女孩的梦幻艺术照片效果。以下是详细步骤: 1. **新建文件与导入素材**: - 首先创建一个新文件,大小与素材1相同,命名为"奔月女孩"。 - 然后打开素材1,...

    奔月生物:2021年半年度报告.PDF

    奔月生物:2021年半年度报告.PDF

    HTML5奔月游戏

    HTML5奔月游戏是一款完全基于HTML5技术开发的互动小游戏,玩家可以扮演一只兔子,目标是在游戏中尽可能地收集月饼并安全抵达月球。这款游戏的亮点在于其无需任何额外的插件或软件支持,只需浏览器就能运行,这得益于...

    H5游戏源码 奔月游戏.zip

    《H5游戏源码解析:奔月游戏》 在当今数字化时代,HTML5(简称H5)技术以其跨平台、轻量级、易部署的特点,成为制作网页游戏的热门选择。"奔月游戏"作为一款H5游戏,其源码为我们提供了一窥H5游戏开发的窗口。本文...

    HTML5奔月游戏 源码.zip

    在奔月游戏中,可能使用canvas来绘制角色、背景、轨迹等元素,并通过定时更新画面来实现动画效果。 2. **Web Audio API**:HTML5提供了Web Audio API,用于处理和播放音频。游戏往往需要背景音乐和音效,这个API...

    HTML5兔子奔月吃月饼游戏源码.zip

    在这个兔子奔月吃月饼游戏中,Canvas可能被用来绘制游戏场景,如月亮、兔子、月饼等元素,以及处理游戏中的动态效果,如兔子跳跃、月饼移动等。 JavaScript是HTML5游戏的核心,它负责处理游戏逻辑、用户交互以及...

    小游戏源码-火贱兔奔月.rar

    【标题】"小游戏源码-火贱兔奔月.rar" 提供的是一个小型游戏的源代码,名为"火贱兔奔月"。这类源码通常用于教学、学习或游戏开发者的参考,帮助开发者理解游戏的基本架构和编程逻辑。 【描述】"小游戏源码-火贱兔...

    HTML5实现兔子中秋奔月吃月饼游戏源码.zip

    在这个兔子奔月吃月饼的游戏中,Canvas可能是用来绘制游戏背景、角色、月饼以及其他交互元素的平台。 CSS3(层叠样式表3)也在这其中扮演了重要角色。CSS3不仅提供了更精细的样式控制,如边框阴影、圆角、过渡和...

    析鲁迅《奔月》.docx

    《鲁迅的《奔月》:颠覆传统,开创审美新向度》 鲁迅的短篇小说《奔月》是其《故事新编》中的一篇,它颠覆了我们对古代神话的传统认知,尤其对嫦娥这一角色的刻画,使得这篇作品在文学史上占据了独特的地位。鲁迅...

    HTML5小游戏【火贱兔奔月-425款经典优秀H5小游戏合集】游戏源码分享下载 - hjby.zip

    游戏源码分享下载 --- hjby.zipHTML5小游戏【火贱兔奔月--425款经典优秀H5小游戏合集】游戏源码分享下载 --- hjby.zipHTML5小游戏【火贱兔奔月--425款经典优秀H5小游戏合集】游戏源码分享下载 --- hjby.zipHTML5小...

    HTML5兔子奔月吃月饼游戏源码.rar

    在这款"兔子奔月吃月饼游戏"中,HTML5起到了核心作用,提供了游戏的基础结构和用户界面。 JavaScript是网页开发中的重要脚本语言,它与HTML5结合,赋予了网页动态功能。在这个游戏中,JavaScript用于处理用户输入,...

    五年级语文《嫦蛾奔月》教案 苏教版.doc

    在中国传统神话故事中,嫦娥奔月的故事可谓家喻户晓,不仅在古代诗人骚客的笔下被多次吟咏,也成为了后代文化教育中不可或缺的一部分。小学五年级语文《嫦娥奔月》教案,正是以此为教材,引导学生们走进这一古老而...

    奔月生物:2021年半年度报告.rar

    【标题】:“奔月生物:2021年半年度报告.rar”是一个压缩文件,其中包含了一份关于奔月生物科技公司在2021年上半年业务运营、财务状况和业绩表现的详细报告。这类报告通常由上市公司发布,以供投资者、分析师和其他...

Global site tag (gtag.js) - Google Analytics