netbeans 配置JPA让PostgreSQL生成主键值

00jrzges  于 2023-04-21  发布在  PostgreSQL
关注(0)|答案(6)|浏览(175)

所以我们的项目使用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,然后在创建过程后获得它?或者有什么更好的解决方案?谢谢。

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

  • 关于答案的简短说明 * 我这边有一个小谎言,因为我们使用了PG管理-〉查看前100行并从那里编辑行,而不是使用select。事实证明,这个编辑器不知何故跳过了更新ID的过程,所以即使在DB中,当我们编写一个正确的INSERT时,它也会用不正确的ID执行!因此,这基本上是我们使用的编辑器的问题,而不是数据库和应用程序的问题。

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

flvtvl50

flvtvl501#

给定表定义:

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

使用Map:

@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_seq是PostgreSQL默认的SERIAL序列命名,我建议您坚持使用它。
如果需要Hibernate与数据库的其他客户端协作,allocationSize=1很重要。
请注意,如果事务回滚,这个序列中将有“间隙”。事务可以由于各种原因回滚。您的应用程序应该设计为科普此问题。

  • 永远不要假设对于任何id n都有一个id n-1n+1
  • 永远不要假设id n是在小于n的id之前或大于n的id之后添加或提交的。而是在表中记录时间戳。
  • 不要对ID进行加减运算。只比较它们是否相等。

参见PostgreSQL documentation for sequences和串行数据类型。
他们解释说,上面的表定义基本上是一个快捷方式:

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,
    ...
)

然后使用(unsafe,do not use)插入:

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

或者(* safe,never do this*):

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

那么 * 你做错了 *,应该切换到一个序列(如上所示)或一个使用锁定计数器表的 * 正确的 * 无间隙序列实现(再次,请参阅上文并参阅Google中的“无间隙序列postgresql”)。如果数据库上有多个连接,则上述两种方法都会出错。

os8fio9y

os8fio9y2#

看起来你必须像这样使用序列生成器:

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

hmae6n7t3#

请尝试使用GenerationType.TABLE而不是GenerationType.IDENTITY。数据库将创建单独的表,用于生成唯一的主键,它还将存储最后使用的ID号。

mf98qq94

mf98qq944#

您也可以通过编写脚本来执行通用GenerationType.IDENTITY到所选答案提出的解决方案的大量转换,从而保存一些精力。下面的脚本对Java源文件的格式有一些轻微的依赖性,并且将在没有备份的情况下进行修改。
运行脚本后:
1.搜索并将import javax.persistence.Table;替换为import javax.persistence.Table; import javax.persistence.SequenceGenerator;
1.在NetBeans中重新格式化源代码,如下所示:
1.选择要格式化的所有源文件。
1.按Alt+Shift+F
1.确认重新格式化。
将以下脚本保存为update-sequences.sh或类似文件:

#!/bin/bash

# Change this to the directory name (package name) where the entities reside.
PACKAGE=com/domain/project/entities

# Change this to the path where the Java source files are located.
cd src/main/java

for i in $(find $PACKAGE/*.java -type f); do
  # Only process classes that have an IDENTITY sequence.
  if grep "GenerationType.IDENTITY" $i > /dev/null; then
    # Extract the table name line.
    LINE_TABLE_NAME=$(grep -m 1 @Table $i | awk '{print $4;}')
    # Trim the quotes (if present).
    TABLE_NAME=${LINE_TABLE_NAME//\"}
    # Trim the comma (if present).
    TABLE_NAME=${TABLE_NAME//,}

    # Extract the column name line.
    LINE_COLUMN_NAME=$(grep -m 1 -C1 -A3 @Id $i | tail -1)
    COLUMN_NAME=$(echo $LINE_COLUMN_NAME | awk '{print $4;}')
    COLUMN_NAME=${COLUMN_NAME//\"}
    COLUMN_NAME=${COLUMN_NAME//,}

    # PostgreSQL sequence name.
    SEQUENCE_NAME="${TABLE_NAME}_${COLUMN_NAME}_seq"

    LINE_SEQ_GENERATOR="@SequenceGenerator( name = \"$SEQUENCE_NAME\", sequenceName = \"$SEQUENCE_NAME\", allocationSize = 1 )"
    LINE_GENERATED_VAL="@GeneratedValue( strategy = GenerationType.SEQUENCE, generator = \"$SEQUENCE_NAME\" )"
    LINE_COLUMN="@Column( name = \"$COLUMN_NAME\", updatable = false )\n"

    # These will depend on source code formatting.
    DELIM_BEGIN="@GeneratedValue( strategy = GenerationType.IDENTITY )"
    # @Basic( optional = false ) is also replaced.
    DELIM_ENDED="@Column( name = \"$COLUMN_NAME\" )"

    # Replace these lines...
    #
    # $DELIM_BEGIN
    # $DELIM_ENDED
    #
    # With these lines...
    #
    # $LINE_SEQ_GENERATOR
    # $LINE_GENERATED_VAL
    # $LINE_COLUMN

    sed -i -n "/$DELIM_BEGIN/{:a;N;/$DELIM_ENDED/!ba;N;s/.*\n/$LINE_SEQ_GENERATOR\n$LINE_GENERATED_VAL\n$LINE_COLUMN/};p" $i
  else
    echo "Skipping $i ..."
  fi
done

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

y3bcpkx1

y3bcpkx15#

对我有用
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;

    // ....

}
aydmsdu9

aydmsdu96#

解决方案

@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)

不工作。这是一个谎言。问题仍然存在。请在发布前审查!!

相关问题