【问题标题】:Unexpected behaviour of EntityManager.merge()EntityManager.merge() 的意外行为
【发布时间】:2013-03-01 19:09:20
【问题描述】:

我正在使用嵌入式 glassfish (3.1.2.2) 和 junit (4.11),JDK 1.7,尽管我的源和目标设置为 1.6(maven-compiler-plugin 配置)。

以下是我的代码:

Person.java

@Entity
public class Person implements Serializable {

    private static final long serialVersionUID = 81398385247591972L;

    @Id
    @GeneratedValue
    private Long id;
    @Version
    private Long version;
    @Column(length = 15, nullable = false, unique = true, updatable = false)
    private String username;
    @Column(length = 50)
    private String status;

    // Constructors

    // getters/setters

    // hashCode, equals, toString

}

Service.java

@Stateless
public class Service {

    @PersistenceContext(unitName = "ExamplePU", type = PersistenceContextType.TRANSACTION)
    private EntityManager em;

    public Person add(Person person) {
        em.persist(person);
        return person;
    }

    public Person find(Long id) {
        return em.find(Person.class, id);
    }

    public Person modify(Person person) {
        return em.merge(person);
    }

    // some more code ...

}

ServiceTest.java

public class ServiceTest {

    private static EJBContainer ejbContainer;
    private static Service service;

    // @BeforeClass, @AfterClass, @Before, @After

    @Test
    public void testMerge() {
        Person person;

        /* Step 1 */person = service.add(new Person("username", "status"));
        print("Added : " + person);

        person.setStatus("Away");
        /* Step 2 */person = service.modify(person);
        print("Merged (status change) : " + person);

        person.setUsername("UsErNaMe");
        /* Step 3 */person = service.modify(person);
        print("Merged (username change) : " + person);
    }

    // Some more tests

}

步骤 1 生成以下 SQL(如预期):

INSERT INTO PERSON (ID, STATUS, USERNAME, VERSION) VALUES (?, ?, ?, ?)
    bind => [1, status, username, 1]

第 2 步生成以下 SQL(如预期):

UPDATE PERSON SET STATUS = ?, VERSION = ? WHERE ((ID = ?) AND (VERSION = ?))
    bind => [Away, 2, 1, 1]

第 3 步 不会生成任何 SQL,但不会引发任何异常,这是我所期待的,因为“用户名”被注释为 @Column(..., 可更新= 假)。 print(...) 方法打印以下输出:

已合并(用户名更改):人员 [id=1,版本=2,用户名=UserNaMe,状态=Away]

这次 merge() 操作更新了用户名,但没有更新版本。此外,现在数据库与 EntityManager 缓存不同步。

这是预期的,还是 EclipseLink 中的错误?

更新

预期结果是上述第 3 步的异常。

更新

已提交错误here

【问题讨论】:

    标签: jpa-2.0 eclipselink glassfish-embedded


    【解决方案1】:

    您将该列标记为不可更新,并且 EclipseLink 检测到对您告诉它合并的人所做的唯一更改是用户名。但不得更新用户名。所以它不会发出任何 SQL 更新查询。

    如果您将列标记为不可更新,则不应更新它。

    因此,为了清楚起见,您观察到的行为是预期的行为。

    【讨论】:

    • 我没有使用 Hibernate。它是 EclipseLink,带有 GlassFish。其次,用户名不可更新是我在第 3 步中期待异常的原因。抱歉,如果我的问题不够清楚。
    • 对不起。我已经习惯了 Hibernate,所以我输入了 Hibernate 而不是 EclipseLink。但这并没有改变任何东西。它们都是 JPA 的实现。你期待一个例外,但你不会得到一个。 updatable=false 仅表示:更新语句必须忽略此列。这并不意味着:“如果我要求您合并实体并且此列已更改,则抛出异常”。
    • 您对“忽略更新语句”部分是正确的。但是它返回的对象现在与数据库不一致(如问题的第 3 步所示)。此外,稍后,如果我执行 find(...),它会返回一个错误的对象,因为这就是它在缓存中的内容。我认为,这是不对的。如果该字段被忽略,则更新语句和合并状态都需要忽略它。
    • 我没有在规范中看到任何关于此的明确规定。对我来说,避免更新不可更新的字段是你的工作。
    • 你是对的,规范没有谈论异常。但令人不安的是,它返回的对象从未持久存在,也永远不会存在。因此,它至少应该返回与数据库一致的未更新对象。至于避免更新成为开发人员的工作,这个注释属性的目的是什么?如果我可以避免更新,我根本不需要这个注释!任何方式,感谢您的投入。我正在为此行为提交错误。 - 阿图尔
    猜你喜欢
    • 1970-01-01
    • 2020-10-04
    • 2016-07-16
    • 2016-05-10
    • 2020-07-23
    • 2021-08-23
    • 2021-11-16
    • 2017-10-20
    • 2016-06-29
    相关资源
    最近更新 更多