配置JPA让PostgreSQL生成主键值

时间:2012-08-06 09:30:25

标签: postgresql java-ee jpa netbeans entity

所以我们的项目使用PostgreSQL数据库,我们使用JPA来操作数据库。 我们使用Netbeans 7.1.2中的自动创建程序从数据库创建了实体。

经过小幅更改后,我们的主键值被描述为:

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Basic(optional = false)
@NotNull
@Column(name = "idwebuser", nullable = false)
private Integer idwebuser;

问题是现在应用程序不灵活,因为当我们直接修改数据库(使用SQL或其他工具)而不是通过Java应用程序时 - 生成的值低于实际的数据库ID值 - 所以我们在创建新实体期间收到错误。

JPA是否有可能让数据库自动生成ID,然后在创建过程后获取它? 或者什么是更好的解决方案? 感谢。

修改 进一步来说: 我们有一个用户表,我的问题是使用任何类型的策略生成类型,JPA正在插入一个由它的生成器id指定的新实体。这对我来说是错误的,因为如果我自己对表进行更改,通过添加新条目,应用程序的GeneratedValue低于当前ID - 这导致我们出现重复ID异常。 我们能解决它吗?)?

答案的简短说明 我身边有一点谎言,因为我们使用了PG管理员 - >从那里查看前100行和编辑行而不是使用选择。但是,事实证明,这个编辑器以某种方式跳过了更新ID的过程,因此即使在DB中,当我们编写一个正确的INSERT时,它也会被执行不正确的ID!所以它基本上是我们使用的编辑器的问题而不是数据库和应用程序......

现在它甚至可以使用@GeneratedValue(strategy=GenerationType.IDENTITY)

5 个答案:

答案 0 :(得分:70)

鉴于表格定义:

CREATE TABLE webuser(
    idwebuser SERIAL PRIMARY KEY,
    ...
)

使用映射:

@Entity
@Table(name="webuser")
class Webuser {

    @Id
    @SequenceGenerator(name="webuser_idwebuser_seq",
                       sequenceName="webuser_idwebuser_seq",
                       allocationSize=1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
                    generator="webuser_idwebuser_seq")
    @Column(name = "idwebuser", updatable=false)
    private Integer id;

    // ....

}

命名tablename_columname_seqSERIAL的PostgreSQL默认序列命名,我建议您坚持使用它。

如果你需要Hibernate与其他客户端合作到数据库,allocationSize=1很重要。

请注意,如果事务回滚,此序列将包含“空白”。由于各种原因,事务可以回滚。您的应用程序应该设计为能够应对此问题。

  • 永远不要假设任何身份n都有一个身份n-1n+1
  • 绝不假设在ID小于n之前或ID大于n之后添加或提交了ID n。如果你对如何使用序列非常小心,你可以做到这一点,但你永远不应该尝试;记录表格中的时间戳。
  • 从不添加或减少ID。比较他们的平等而不是别的。

请参阅PostgreSQL documentation for sequencesthe serial data types

他们解释说上面的表定义基本上是:

的快捷方式
CREATE SEQUENCE idwebuser_id_seq;
CREATE TABLE webuser(
    idwebuser integer primary key default nextval('idwebuser_id_seq'),
    ...
)
ALTER SEQUENCE idwebuser_id_seq OWNED BY webuser.idwebuser;

...这应该有助于解释为什么我们添加了@SequenceGenerator注释来描述序列。


如果您确实必须有无间隙序列(例如,检查或发票编号),请参阅gapless sequences但请认真考虑,请避免使用此设计,并且从不将其用作主键


注意:如果您的表格定义如此:

CREATE TABLE webuser(
    idwebuser integer primary key,
    ...
)

并使用(不安全,不要使用)插入其中:

INSERT INTO webuser(idwebuser, ...) VALUES ( 
    (SELECT max(idwebuser) FROM webuser)+1, ...
);

或(不安全,从不这样做):

INSERT INTO webuser(idwebuser, ...) VALUES ( 
    (SELECT count(idwebuser) FROM webuser), ...
);

然后你做错了并且应该使用锁定的计数器表切换到序列(如上所示)或正确的无间隙序列实现(同样,见上文并参见Google中的“无间隙序列postgresql”。如果在数据库上有多个连接工作,则上述操作都是错误的。

答案 1 :(得分:2)

似乎你必须使用序列生成器,如:

@GeneratedValue(generator="YOUR_SEQ",strategy=GenerationType.SEQUENCE)

答案 2 :(得分:1)

请尝试使用GenerationType.TABLE代替GenerationType.IDENTITY。数据库将创建单独的表,用于生成唯一的主键,它还将存储上次使用的ID号。

答案 3 :(得分:0)

您还可以通过编写脚本来执行通用$(document).ready(function() { $('nav li a i:first').on('click', function() { $('.forperson1').slideToggle(); $('nav').toggleClass('open closed'); }); }); 到所选答案提议的解决方案的批量转换,从而节省一些工作。下面的脚本对Java源文件的格式有一些轻微的依赖关系,并且无需备份即可进行修改。注意事项!

运行脚本后:

  1. 搜索并替换GenerationType.IDENTITY import javax.persistence.Table;
  2. 重新格式化NetBeans中的源代码,如下所示:
    1. 选择要格式化的所有源文件。
    2. Alt + Shift + F
    3. 确认重新格式化。
  3. 将以下脚本保存为import javax.persistence.Table; import javax.persistence.SequenceGenerator;或类似名称:

    update-sequences.sh

    使用NetBeans生成CRUD应用程序时,ID属性将不包含可编辑的输入字段。

答案 4 :(得分:0)

对我有用

  1. 像这样创建表,使用SERIAL。
CREATE TABLE webuser(
    idwebuser SERIAL PRIMARY KEY,
    ...
)
  1. 在id字段中添加@GeneratedValue(strategy = GenerationType.IDENTITY)。
@Entity
@Table(name="webuser")
class Webuser {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    // ....

}