当前位置: 首页 > 知识库问答 >
问题:

升级后,@mapsid在保存现有实体时抛出一个错误,但其他方面工作正常

武友樵
2023-03-14

我正在将Spring Boot 1.5.21项目(Java 8U221)升级到Spring Boot 2.1.9(Java 11.0.2-Open)。在这两种情况下,我们都使用带有Spring Boot启动程序和依赖关系解析器的gradle构建,因此底层spring、JPA和Hibernate库的版本都是spring管理的。

该项目有一个可选的一对一映射,其中子实体从父实体生成的ID获取其ID。在项目的Spring Boot1版本中,关系配置如下:

@Entity
@Table(name = "PARENT_OBJECT", schema = "MYSCHEMA")
public class ParentObject implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PARENT_OBJECT_ID_SEQ")
    @SequenceGenerator(name = "PARENT_OBJECT_ID_SEQ", sequenceName = "MYSCHEMA.PARENT_OBJECT_ID_SEQ")
    protected Long id;

    @OneToOne(optional = true, mappedBy = "parentObject", cascade = CascadeType.ALL, orphanRemoval = true)
    @Valid
    protected ChildObject childObject;

    // Other fields and methods
}

@Entity
@Table(name = "CHILD_OBJECT", schema = "MYSCHEMA")
public class ChildObject implements Serializable {
    @Id
    @Column(name = "parent_object_id", unique = true, nullable = false, insertable = true, updatable = false)
    private Long parentObjectId;

    @OneToOne
    @MapsId
    @PrimaryKeyJoinColumn
    @JsonIgnore
    private ParentObject parentObject;

    // Other fields and methods
}

当我更新到Spring Boot2时,我必须更新很多JPA设置,特别是有一点被抱怨的是,生成的查询正在查找'parentobject_id'而不是'parent_object_id',所以我对其进行了一些研究,找到了一篇解释如何用@joincolumn修复一对一映射的列名的文章,并将子html" target="_blank">对象的parentObject字段的注释更新为:

@OneToOne
@MapsId
@JoinColumn(name = "parent_object_id")
@JsonIgnore
private ParentObject parentObject;

我完全删除了@primarykeyjoincolumn,因为文档似乎建议,如果您希望Hibernate处理ID的分配,并且如果您要自己管理这些ID,那么就应该使用@primarykeyjoincolumn

这个配置在我的所有测试中都运行良好,除了2:控制器的更新集成测试(当ParentObject首次与相关联的ChildObject一起创建时,POST测试也运行良好)。对于这两个测试,我得到的错误是:

JPasystemException:试图从null一对一属性[org.MyCompany.MyProject.MyModule.MySubModule.ChildObject.ParentObject]中分配id;嵌套异常为org.hibernate.id.IdentifierGenerationException:尝试从null一对一属性[org.MyCompany.MyProject.MyModule.MySubModule.ChildObject.ParentObject]分配id

奇怪的是,这些实体及其关联的所有存储库集成测试都通过了,所有其他控制器集成测试也都通过了,包括一个新的ParentObject与它的新的ChildObject一起发布的测试。我到处寻找一个可能的解决方案,但我读到的每一篇文章似乎都暗示我使用的配置应该可以工作。我还尝试了其他配置,这些配置建议使用@PrimaryKeyJoinColumn和自己设置ID字段,在ChildObject的ID上使用ID生成器(尽管@MapSID的目的是告诉系统使用ParentObject的ID),但通常我最终得到的结果是更多的测试失败,出现以下错误:

JPasystemException:在调用save()之前必须手动分配该类的ID:org.myCompany.myProject.myModule.mySubModule.ChildObject;嵌套异常为org.hibernate.id.IdentifierGenerationException:调用save()之前必须手动分配该类的ID:org.myCompany.myProject.myModule.mySubModule.ChildObject

虽然有时我会得到最初的错误,即不能‘从null分配id’。在这一点上,我不知道这是如何配置不正确的,或者是什么其他因素在搅乱事情。在这一点上,我愿意尝试任何和所有的建议。

为了完整起见,下面是应用程序中的一些代码片段。堆栈跟踪中的相关行号会被注释。我省略了控制器代码,因为它没有什么特别之处;POST和PUT方法都调用相同的服务方法;它们只是在不同的endpoint上,PUT首先检查对象是否存在于DB中,然后调用service save方法。下面是为ParentObject调用JPA repo的服务方法:

@Service
@Transactional(readOnly = true)
public class ParentObjectServiceImpl implements ParentObjectService {
    // other fields and other methods

    @Autowired
    private ParentObjectRepository parentObjectRepository;

    @Transactional(readOnly = false)
    @Override
    public ParentObject saveParentObject(final ParentObject parentObject) {
        parentObject.prepForPersistence();
        return parentObjectRepository.save(parentObject); //Line 93
    }
}

下面是我的JPA存储库:

public interface ParentObjectRepository extends CrudRepository<ParentObject, Long> {
    // custom methods, no override for save though
}
public void prepForPersistence() {
    if(childObject != null) {
        childObject.setParentObject(this);
        // In some iterations of the code in trying to solve this, I also had the following line
        //childObject.setParentObjectId(this.id);
    }
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyApplication.class)
@WebAppConfiguration
// This profile disabled csrf for testing, and sets some env variables
@ActiveProfiles("integration-test")
@Transactional
public class ParentObjectControllerTest {
    @Autowired
    private WebApplicationContext context;

    private MockMvc mockMvc;

    @Before
    public void setUp() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(context)
            .apply(springSecurity())
            .build();
    } 

    // This test PASSES
    @Test
    @WithMockUser(roles = {"MY_APP_ADMIN"}, username = TEST_USER)
    @Sql(scripts = "/db-scripts/bootstrap.sql")
    public void testPostParentObjectWithChildObject() {
        final ChildObject childObject = new ChildObject();
        // set some properties on childObject that don't relate to ParentObject

        final ParentObject parentObject = new ParentObject();
        // set some properties on parentObject that don't relate to ChildObject
        parentObject.setChildObject(childObject);

        final ParentObject result = given().mockMvc(mockMvc).contentType(ContentType.JSON)
            .and().body(item).log().all()
            .when().post("/parent-objects")
            .then().log().all().statusCode(201).contentType(ContentType.JSON)
            .and().body(matchesJsonSchemaInClasspath("json-schemas/parent-object.json"))
            .and().body("username", equalTo(TEST_USER))
            .extract().as(ParentObject.class);

        assertThat(result.getId(), is(notNullValue()));
        assertThat(result.getChildObject().getParentObjectId(), is(result.getId()));
    }

    // This test FAILS with the 'attempted to assign id from null one-to-one property' error
    @Test
    @WithMockUser(roles = {"MY_APP_ADMIN"}, username = TEST_USER)
    // Inserts a ParentObject record with ID -1
    @Sql(scripts = "/db-scripts/bootstrap.sql")
    public void testPutParentObjectWithChildObject() {
        final ChildObject childObject = new ChildObject();
        // set some properties on childObject that don't relate to ParentObject

        final Long EXISTING_ID = -1L;
        final ParentObject parentObject = new ParentObject();
        parentObject.setId(EXISTING_ID);
        parentObject.setChildObject(childObject);

        final ParentObject result = given().mockMvc(mockMvc).contentType(ContentType.JSON)
            .and().body(item).log().all()
            .when().put("/parent-objects/{parentObjectId}", parentObject.getId()) //line 260
            .then().log().all().statusCode(200)
            .extract().as(ParentObject.class);

        assertThat(result.getId(), is(EXISTING_ID));
        assertThat(result.getChildObject().getParentObjectId(), is(EXISTING_ID));
}

提前感谢大家的建议和帮助!

共有1个答案

柯梓
2023-03-14

我认为这是一个bug,但我仍然感兴趣的变通办法,如果有人有他们。我最初在hibernate上发现了一个似乎与问题相符的bug,但它被注册为只影响5.2系列,并被标记为“已修复”,由于我们的项目使用的是5.3系列,所以我继续了。但是,从spring方面深入研究提交的bug,一条面包屑的线索将我引向了这个5.3系列的bug:https://hibernate.atlassian.net/browse/hhh-13413,它链接回了我最初发现的5.2系列bug,与5.3的文章相反,它似乎暗示了这从5.2系列开始就一直存在问题,并且在5.3系列中从来不应该起作用,因为不仅5.3系列的bug被标记为5.2系列bug的重复,当我仔细查看5.2 bug时,它声明它的修复版本直到5.4:https://hibernate.atlassian.net/browse/hhh-12436

所以我会试着降级到上一个5.2系列,然后升级到5.4系列...我只是不确定这两种方法将如何使用spring data jpa的其余功能,这本身可能是一个大问题。

更新:除了因为我们使用Java11(https://hibernate.atlassian.net/browse/hhh-12924->I had to use javassist:3.23.0-ga to get hibernate-core:5.2.13.final to work)而遇到问题之外,降级成功地使一对一映射再次工作。所有其他测试也都通过了。但是,我还没有对这个解决方案进行过广泛的测试,因为我更喜欢基于升级的解决方案。

更新2:能够升级到Spring Boot2.2.0(它使用Hibernate5.4,这是一对一映射的特殊bug被修复的地方)!它需要对我的序列进行一些更改,但升级并不是太痛苦;甚至不需要使用降级标志!如果您的映射问题没有得到解决,那么就会有一个“使用5.2行为”标志,因为他们仍然在解决各种映射场景的矛盾。有关5.4迁移笔记的详细信息,请访问:https://github.com/hibernate/hibernate-orm/blob/61cddad76d5bba951805fa7ed90cc149d404841c/migration-guide.adoc

 类似资料:
  • 去年我在做一个项目,导出和导入都很好,但最近我决定导入我的sql文件,由于每个表中都有很多错误,我无法导入。 一旦我停止工作,我就删除了xampp,现在决定再次下载它,但我被困在导入上,这是我的完整文件: http://pastebin.com/mddVUU1i 我的错误如下: 创建表(int(11)Not NULL, varchar(50)Not NULL,datetime Not NULL D

  • 输出给我抛出了这个错误: 我已经尝试按照OWL API示例中的说明进行保存,所以我不知道我的错误在哪里。 这是我用来保存本体的代码: 和getPatient的代码

  • 我正在做一个简单的部分更新场景,它适用于Solr的6.x和7.x版本。将Solr和Solrj升级到8.8后,我遇到以下异常: 注意,这与前面关于stackoverflow的问题不同,因为我传递的是简单的整数字段,而在Solr/Lucene端,它被替换。

  • 运行Firebase upgrade命令后,我尝试在模拟器中运行我的应用程序,出现了以下错误消息: 在调试模式下在 sdk gphone64 x86 64 上启动 lib\main.dart...运行 Gradle 任务 'assembleDebug'... /C:/src/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core-1.24.

  • 我们正试图在MVC4控制器中的一种操作方法中防止跨站点请求伪造攻击。下面是我们所做的代码更改,它在VS 2013中运行良好,但在VS 2012中抛出错误,与我们在Windows Server 2012-IIS 8中部署时遇到的错误相同。 View.cshtml Controller.cs VS2013-以上代码运行良好 VS2012-引发以下错误 http://schemas.xmlsoap.or

  • 我们正在应用程序中进行更改,将文件扩展名从xls转换为xlsx格式(2007及以上),同时从应用程序中导出文件。 通过升级到POI3.9JAR,我添加了HSSF的XSSF intead,从而进行了必要的代码更改。我们使用的Jar文件的前一个版本是POI3.0。 以下是我在库文件夹中添加的JAR: