通过为多个实体共享Hibernate序列生成器而感到困惑

时间:2016-01-04 17:22:52

标签: java mysql hibernate

我的课程使用像

这样的ID
@Id @Generated(GenerationTime.INSERT) @GeneratedValue private Integer id;

这适用于H2(支持序列),并通过创建帮助程序表hibernate_sequence来解释MySql。使用this answer,一切看起来都像我想要的那样,特别是对所有表使用单个序列。

有一件事似乎是错误的:帮助程序表中有多行。我的id@MappedSuperclass中声明,在初始化期间,为每个具体类执行此行:

insert into hibernate_sequence values ( 1 )

这显然是错误的:每个表都有一行,每个包含相同的值(最初一个;当更改时,它们都以相同的方式更改,因为SQL为update hibernate_sequence set next_val=? where next_val=?,因此它会影响所有行以相同的方式)。

这是无害的,但我想知道:这是一个错误还是我做错了什么?

3 个答案:

答案 0 :(得分:4)

如果你想让它成功,你现在需要使用其他策略:

@GenericGenerator(
        name = "table_generator",
        strategy = "org.hibernate.id.enhanced.TableGenerator"
)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator")

org.hibernate.id.enhanced.SequenceStyleGenerator

怎么样?

我认为hibernate如何初始化共享单行序列表存在问题。

对于hibernare 5.0.6.Final问题的根源在于org.hibernate.boot.internal.InFlightMetadataCollectorImpl类 在

private void processExportableProducers(MetadataBuildingContext buildingContext) {
    // for now we only handle id generators as ExportableProducers

    final Dialect dialect = getDatabase().getJdbcEnvironment().getDialect();
    final String defaultCatalog = extractName( getDatabase().getDefaultNamespace().getName().getCatalog(), dialect );
    final String defaultSchema = extractName( getDatabase().getDefaultNamespace().getName().getSchema(), dialect );

    for ( PersistentClass entityBinding : entityBindingMap.values() ) {
        if ( entityBinding.isInherited() ) {
            continue;
        }

        // ***************************************************************************
        // For Instance, it does not filter out the same entityBinding.getIdentifier()
        // and make initialization multiple time
        // ***************************************************************************
        handleIdentifierValueBinding(
                entityBinding.getIdentifier(),
                dialect,
                defaultCatalog,
                defaultSchema,
                (RootClass) entityBinding
        );
    }

    for ( Collection collection : collectionBindingMap.values() ) {
        if ( !IdentifierCollection.class.isInstance( collection ) ) {
            continue;
        }

        handleIdentifierValueBinding(
                ( (IdentifierCollection) collection ).getIdentifier(),
                dialect,
                defaultCatalog,
                defaultSchema,
                null
        );
    }
}

答案 1 :(得分:0)

使用@MappedSuperclass注释的类没有自己的表。只有继承该类的实体才有表。因此,根据初始化没有错误,因为,每个具体实体的顺序应该是不同的。所以这不是一个错误,而是预期的行为。

关于更新,如果在SINGLE表中插入记录时所有三行都得到更新,那么肯定存在错误。但我不确定这是否是您遇到的情况。

答案 2 :(得分:0)

相同的序列生成器

假设有Base类注释@MappedSuperclass。还有AB类扩展Base类;如果您使用IdBase类中注释@SequenceGenerator字段,则Base的所有子类共享相同的序列生成器,并将增加/使用相同的序列在数据库中为他们的ID。这在当然是无害的,但会导致ids的数字难看:

@MappedSuperclass
public class Person {    
  @Id
  @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="id_gen")
  @SequenceGenerator(name="id_gen", sequenceName="a_seq", allocationSize=1)
  private Long id;
}

@Entity
public class A extends Base {
}

@Entity
public class B extends Base {
}

这是他们的ID在添加A,然后是B,然后是A:

之后的样子
A{id=1, name='...'}
B{id=2, name='...'}
A{id=3, name='...'}

不同的序列生成器

一种更好的方法是为每个表设置一个新序列,可以通过为不同的子类分配不同的序列生成器来实现:

@MappedSuperclass
public class Person {    
    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="id_gen")
    private Long id;
}

@Entity
@SequenceGenerator(name="id_gen", sequenceName="a_seq", allocationSize=1)
public class A extends Base {
}

@Entity
@SequenceGenerator(name="id_gen", sequenceName="b_seq", allocationSize=1)
public class B extends Base {
}

在添加A,然后是B,然后是A:

之后,它们的ID将如下所示
A{id=1, name='...'}
B{id=1, name='...'}
A{id=2, name='...'}