配置Hibernate使用Oracle的SYS_GUID()作为主键

7

我正在寻找一种方法让Hibernate在插入新行时使用Oracle的SYS_GUID()函数。 目前我的数据库表将SYS_GUID()设置为默认值,因此如果Hibernate生成省略该值的SQL,则应该可以工作。

我已经使所有内容运行良好,但目前它正在使用系统UUID生成器在代码中生成UUID / GUID:

@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
@Column(name = "PRODUCT_ID", unique = true, nullable = false)
public String getId() {
    return this.productId;
}

这很好,但我希望GUID是由数据库生成的,这样它们将是连续的,并且可能具有更好的性能。此外,我只想知道如何配置它。
我正在使用注解进行配置,但XML配置示例也很棒。
以下是一个示例表定义(如果有影响的话):
CREATE TABLE SCHEMA_NAME.PRODUCT
(
    PRODUCT_ID RAW(16) DEFAULT SYS_GUID() NOT NULL,
    PRODUCT_CODE VARCHAR2(10 CHAR) NOT NULL,
    PRODUCT_NAME VARCHAR2(30 CHAR) NOT NULL,
    PRODUCT_DESC VARCHAR2(512 CHAR)
)

更新:

Mat使用“guid”的解决方案成功,以下是生成的SQL语句:

Hibernate: 
    select rawtohex(sys_guid()) 
    from dual
Hibernate: 
    insert into PRODUCT
    (PRODUCT_CODE, PRODUCT_DESC, LOB_ID, PRODUCT_NAME, PROVIDER_ID, PRODUCT_ID) 
    values (?, ?, ?, ?, ?, ?)

似乎无法在插入时使用列的默认值,所以选择是在应用程序生成GUID和进行数据库往返之间做出。

3个回答

5
你可能可以使用“guid”生成器。请参见Hibernate论坛中的this post。看起来他们在一段时间前使用SYS_GUID()添加了对Oracle的支持,但是文档仍然说他们只支持SQL Server和MySQL。
我还没有使用JPA注释,但这里有一个使用XML配置的示例:
<id name="PRODUCT_ID">
  <generator class="guid" />
</id>

编辑:关于你的第二个问题,我认为你是在问为什么Hibernate不能做这样的事情:

INSERT INTO PRODUCT (PRODUCT_ID, /* etc */)
SELECT SYSGUID(), /* etc */

原因在于Hibernate必须知道对象的ID。例如,考虑以下情况:
  1. 您创建一个新的产品对象并保存它。Oracle分配ID。
  2. 您将Product从Hibernate会话中分离。
  3. 您稍后重新附加它并进行一些更改。
  4. 现在您想要持久化这些更改。
如果Hibernate不知道ID,则无法执行此操作。它需要ID才能发出UPDATE语句。因此,org.hibernate.id.GUIDGenerator的实现必须预先生成ID,然后在INSERT语句中重用它。
这也是为什么如果使用数据库生成的ID(包括支持自动递增的数据库)Hibernate不能进行任何批处理的原因。使用hilo生成器或其他Hibernate生成的ID机制是一次性插入多个对象时获得良好性能的唯一方法。

这个有效,但会产生一些额外的SQL语句,请看我的更新。感谢您的时间。 - Ryan Cook
1
嗨,Ryan。我编辑了我的答案,试图回答你的后续问题。 - Matt Solnit
3
FYI:通过注释实现这个很简单:@GenericGenerator(name = "guid", strategy = "guid") - Ryan Cook
对我来说,我必须提供两个注释@GeneratedValue(generator=”db-guid”) @GenericGenerator(name=”db-guid”, strategy = “guid”) - The PowerHouse

2

我有一个和主题发起者相同的任务。感谢@Matt Solnit的建议,我使用了这样的注释:

@Id
@NotNull
@Column(name = "UUID")
@GenericGenerator(name = "db-uuid", strategy = "guid")
@GeneratedValue(generator = "db-uuid")
private String uuid;
public String getUuid() { return uuid; }
public void setUuid(String uuid) { this.uuid = uuid; }

strategy = "guid"String类型是解决方案的重要部分。

在持久化新实体之前,Hibernate会发出SQL查询:

select rawtohex(sys_guid()) from dual

我的设置:Oracle 11、Hibernate 4.3.4.Final、Spring 3.2.x。表中的字段为raw(16),以实现高效存储和较小的索引大小,而不是使用char(32)
当我尝试将java.util.UUID用作ID字段类型时,Hibernate在持久化新实体时会出错(它试图将String类型设置为java.util.UUID字段)。
此外,我在非Hibernate查询(Spring JDBC助手)中使用javax.xml.bind.DatatypeConverter,将其转换为byte[]进行传递。
String query = "insert into TBL (UUID, COMPANY) values (:UUID, :COMPANY)";
MapSqlParameterSource parameters = new MapSqlParameterSource()
    .addValue("COMPANY", repo.getCompany())
    .addValue("UUID", DatatypeConverter.parseHexBinary(signal.getUuid()));
namedJdbcTemplate.update(query, parameters);

用于提取:

ResultSet rs;
sig.id = DatatypeConverter.printHexBinary(rs.getBytes("UUID"));

所有的web控制器都会得到类似以下的代码:
025131763FB19522E050010A106D11E9

没有使用 {, -, } 字符的 UUID 表示法(通常的表示法是 {a-b-c-d-x-y}),已经进行了 URL 编码并且是安全的。您不需要为 String 类型实现 PropertyEditorConverter
@RequestMapping(value = {"/signal/edit/{id}.htm"}, method = RequestMethod.POST)
public String handleEditRequest(
        @PathVariable("id") String id,

与使用 jaa.util.UUID 失败的尝试相比,我需要编写以下内容:
@Component
public static class UUIDPropertyEditor extends PropertyEditorSupport {
    @Override
    public void setAsText(final String str) {
        if (str == null || str.isEmpty()) {
            setValue(null);
            return;
        }
        setValue(UUID.fromString(str));
    }
}
private @Autowired UUIDPropertyEditor juuidPE;

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(UUIDPropertyEditor.class, juuidPE);
}

为了使用:

@PathVariable("id") UUID id,

0

我认为你可以通过将生成器设置为本地来实现。我不太确定在Hibernate中如何实现,但在NHibernate中,你可以在XML中这样做:

<id column="PRODUCT_ID">
  <generator class="native"/>
</id>

2
"native" 是 Oracle 中的序列。因此,我有所怀疑。但尝试一下也不是坏主意。 - Adeel Ansari
顺便提一下,您指定了在NHibernate中执行此操作,但没有指定数据库,这是主要的关注点。 - Adeel Ansari
很有可能我是错的,因为我刚开始使用NHibernate。 - cdmckay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接