Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java-JPA-@Version注释_Java_Jpa_Jpa Annotations - Fatal编程技术网

Java-JPA-@Version注释

Java-JPA-@Version注释,java,jpa,jpa-annotations,Java,Jpa,Jpa Annotations,@Version注释在JPA中是如何工作的 我找到了各种答案,其摘要如下: JPA使用实体中的版本字段来检测对同一数据存储记录的并发修改。当JPA运行时检测到试图同时修改同一记录时,它会向试图最后提交的事务抛出异常 但我仍然不确定它是如何工作的 也可以从以下行开始: 你应该考虑不可变的版本字段。更改字段值会产生未定义的结果 这是否意味着我们应该将版本字段声明为final?每当数据库中的实体更新时,版本字段将增加一个。更新数据库中实体的每个操作都会将从数据库加载的版本添加到其查询中 在检查操作中

@Version
注释在JPA中是如何工作的

我找到了各种答案,其摘要如下:

JPA使用实体中的版本字段来检测对同一数据存储记录的并发修改。当JPA运行时检测到试图同时修改同一记录时,它会向试图最后提交的事务抛出异常

但我仍然不确定它是如何工作的


也可以从以下行开始:

你应该考虑不可变的版本字段。更改字段值会产生未定义的结果


这是否意味着我们应该将版本字段声明为
final

每当数据库中的实体更新时,版本字段将增加一个。更新数据库中实体的每个操作都会将从数据库加载的版本添加到其查询中

在检查操作中受影响的行时,jpa框架可以确保在加载和持久化实体之间没有并发修改,因为当在加载和持久化之间增加实体的版本号时,查询在数据库中找不到实体

但我仍然不确定它是如何工作的

假设一个实体
MyEntity
有一个带注释的
version
属性:

@Entity
public class MyEntity implements Serializable {    

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @Version
    private Long version;

    //...
}
更新时,用
@Version
注释的字段将递增并添加到
WHERE
子句中,如下所示:

UPDATE MYENTITY SET ..., VERSION = VERSION + 1 WHERE ((ID = ?) AND (VERSION = ?))
如果
WHERE
子句未能匹配记录(因为同一实体已被另一个线程更新),则持久性提供程序将抛出
OptimisticLockException

这是否意味着我们应该将版本字段声明为final


不,但是你可以考虑让这个设定器保护你不应该调用它。 用于确保一次只更新一次的版本。JPA提供者将检查版本,如果预期版本已经增加,那么其他人已经更新了实体,因此将引发异常

因此,更新实体值将更安全、更乐观


如果值频繁更改,则可能会考虑不使用版本字段。例如“具有计数器字段的实体,每次访问网页时都会增加”

尽管@Pascal answer完全有效,但根据我的经验,我发现下面的代码有助于实现乐观锁定:

@Entity
public class MyEntity implements Serializable {    
    // ...

    @Version
    @Column(name = "optlock", columnDefinition = "integer DEFAULT 0", nullable = false)
    private long version = 0L;

    // ...
}
为什么??因为:

  • 如果使用
    @Version
    注释的字段意外设置为
    null
    ,乐观锁定将不起作用
  • 由于这个特殊字段不一定是对象的业务版本,为了避免误导,我更喜欢将此类字段命名为
    optlock
    而不是
    version

  • 如果应用程序仅使用将数据插入数据库,则第一点无关紧要,因为JPA供应商将在创建时为
    @version
    字段强制执行
    0
    。但是几乎总是使用简单的SQL语句(至少在单元和集成测试期间)。

    只需添加一点更多信息

    JPA在后台为您管理版本,但是当您通过
    JPAUpdateClause
    更新记录时,它不会这样做,在这种情况下,您需要手动将版本增量添加到查询中

    关于通过JPQL更新,也可以这样说,即不是对实体的简单更改,而是对数据库的更新命令,即使这是由hibernate完成的


    佩德罗

    我在这段代码中遇到了一个空指针异常,因为Long初始化为null,可能应该用0L进行显式初始化。不要依赖于将其自动解装箱到
    Long
    或只是对其调用
    longValue()
    。您需要显式的
    null
    检查。我不会自己显式初始化它,因为这个字段应该由ORM提供程序管理。我认为它在第一次插入数据库时被设置为
    0L
    ,因此如果它被设置为
    null
    ,这会告诉您该记录尚未被持久化。这会阻止创建一个被(尝试)多次创建的实体吗?我的意思是,假设JMS主题消息触发实体的创建,并且有多个应用程序实例侦听该消息。我只是想避免唯一的约束冲突错误..抱歉,我意识到这是一个旧线程,@Version是否也考虑了集群环境中的脏缓存?如果使用Spring Data JPA,那么对于
    @Version
    字段,最好使用包装
    Long
    ,而不是使用原语
    Long
    ,因为
    SimpleJpaRepository.save(实体)
    可能会将空版本字段视为实体是新的指示器。如果实体是新的(如版本为null所示),Spring将调用
    em.persist(实体)
    。但若版本有一个值,那个么它将调用
    em.merge(entity)
    。这也是为什么声明为包装类型的版本字段应该保持未初始化状态的原因。我将其称为
    u\u lmod
    (上次修改的用户),其中用户可以是人或定义的(自动的)流程/实体/应用程序(因此额外存储
    u\u lmod\u id
    )。(在我看来,这是一个非常基本的业务元属性)。它通常将其值存储为UTC中的日期(时间)(表示关于年…毫秒的信息)(如果编辑器的时区“位置”很重要,则使用时区存储)。另外,对于DWH同步场景非常有用。我认为应该在db类型中使用bigint而不是整数来匹配长java类型。很好的一点是Dmitry,谢谢,尽管在IMHO版本控制方面这并不重要。嗨,我遇到过这样一种情况,我在现有字段中引入了@Version,而该字段中有空值(旧记录)。因此,如果旧记录被修改