【问题标题】:Save parent and child entities at same with a notnull foreignkey towards the parent (Spring JPA / Hibernate)使用非空外键将父实体和子实体同时保存到父实体(Spring JPA / Hibernate)
【发布时间】:2017-07-21 10:19:06
【问题描述】:

我在 MySQL 数据库中有两个实体,我想实现双向映射,并在保存新父项时自动保存新子项。

MACCHINA(父)字段:id、marca

PERSONA(子)字段:id、name、macchina_id(外键 NOT NULL)

当我保存一个新的 MACCHINA 时,我还想通过这个 JSON 保存一个新的 PERSONA:

{
  "marca": "string",
  "personas": [
    {
      "nome": "string"
    }
  ]
}

MACCHINA 实体:

@Entity
@Table(name = "macchina")
public class Macchina implements Serializable {

    private static final long serialVersionUID = 1L;

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

    @NotNull
    @Column(name = "marca", nullable = false)
    private String marca;

    @OneToMany(mappedBy = "macchina", cascade = CascadeType.ALL)
    private Set<Persona> personas = new HashSet<>();

    // getters and setters ...
}

PERSONA 实体:

@Entity
@Table(name = "persona")
public class Persona implements Serializable {

    private static final long serialVersionUID = 1L;

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

    @NotNull
    @Column(name = "nome", nullable = false)
    private String nome;

    @ManyToOne(optional = false)
    @JoinColumn(name="macchina_id", referencedColumnName = "id", nullable = false)
    private Macchina macchina;

        // getters and setters ...
    }

在这种情况下,当我在 Macchina 实体上调用 JPA 存储库方法 .save() 时,出现异常:

> Caused by:
> com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
> Column 'macchina_id' cannot be null

在同样的场景中,我尝试在数据库中删除 Persona 表中字段“macchina_id”的 NotNull 约束;在这种情况下,事务已执行,但 Persona 表中的“macchina_id”字段为 NULL。

我找到了一种解决方法,方法是删除数据库中“macchina_id”的 NotNull 约束(以及实体中的注释),并以这种方式修改从父级到子级的映射:

@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "macchina_id")
private Set<Persona> personas = new HashSet<>();

我删除了“mappedBy”并添加了@JoinColumn。 它以这种方式工作:Hibernate 在 Macchina 中执行插入操作,在 Persona 中执行插入操作,最后在 Persona 中执行更新(我猜是编写/更新 macchina_id 字段)。

我的目标是维护数据库中字段“macchina_id”的 NotNull 属性;在映射字段private Macchina macchina; 上保留子实体中的一些属性值,例如 @NotNull / nullable = false / @ManyToOne(optional = false) 并同时保存两个实体,并使用 Spring 自动验证的“macchina_id”字段/休眠,无需手动编写代码。

那么,有一种自动方式(Spring/Hibernate)先保存父级,然后是对父级具有 NotNull 外键的子级?

有什么建议吗?

问候,安德里亚

【问题讨论】:

  • 在保存Macchina之前,将Macchina添加到每个PERSONA,如果你只想保存,还要设置CascadeType.AllPersist
  • 向我们展示您的代码如何保存
  • 显示在新答案中
  • 如前所述,在保存Macchina 之前,将Macchina 添加到每个PERSONA
  • 也显示macchinaMapper.toEntity(macchinaDTO);的代码

标签: hibernate spring-data-jpa one-to-many many-to-one bidirectional-relation


【解决方案1】:

我遇到了确切同样的问题,经过多次搜索找到了这个帖子。我终于解决了。 基本上,JPA(或 Hibernate - 不确定)似乎无法自行设置子级的父引用(即使您设置了注释)。因此,在 ParentEntity 中,您应该执行以下操作:

@Entity
public class ParentEntity {

    @OneToMany(mappedBy = "parentReference", cascade = CascadeType.ALL) // parentReference, the property in ChildEntity class
    private final List<ChildEntity> childEntities = new ArrayList<>();

    public ParentEntity(ParentDTO parentDTO) {
        ...
        this.childEntities.add(new ChildEntity(parentDTO.getChildDTO(), this)); // NOTE second param "this"
    }
}

并在ChildEntity中,设置父引用

@Entity
public class ChildEntity {

    @ManyToOne
    @JoinColumn(name = "child_foreign_key_column_name", nullable = false)
    private final ParentEntity parentReference;

    public ChildEntity(ChildDTO child, ParentEntity parentReference) {
        ...
        this.parentReference = parentReference;
    }
}

【讨论】:

    【解决方案2】:

    用于保存的代码非常简单:我从 REST 接收 JSON 到 DTO,然后将 DTO 映射到域对象,最后调用 JpaRepository 的方法 .save()。

    @Override
    @Transactional
    public MacchinaDTO save(MacchinaDTO macchinaDTO) {
        log.debug("Request to save Macchina : {}", macchinaDTO);
        Macchina macchina = macchinaMapper.toEntity(macchinaDTO);
        macchina = macchinaRepository.save(macchina);
        return macchinaMapper.toDto(macchina);
    }
    

    【讨论】:

    • 将此添加到您的问题中
    【解决方案3】:

    我想尽量避免手动将Macchina 添加到每个Persona。我正在尝试通过 Spring/Hibernate 来管理它(因为如果我从外键中删除 not null 约束,它会起作用)。

    为了做这个例子,我使用了一个由项目生成器 JHipster 生成的项目(如果你知道的话),它使用 MapStruct 来映射 DTO 和域:

    /**
     * Contract for a generic dto to entity mapper.
     @param <D> - DTO type parameter.
     @param <E> - Entity type parameter.
     */
    
    public interface EntityMapper <D, E> {
        public E toEntity(D dto);
        public D toDto(E entity);
        public List <E> toEntity(List<D> dtoList);
        public List <D> toDto(List<E> entityList);
    }
    
    /**
     * Mapper for the entity Macchina and its DTO MacchinaDTO.
     */
    @Mapper(componentModel = "spring", uses = {PersonaMapper.class})
    public interface MacchinaMapper extends EntityMapper <MacchinaDTO, Macchina> {
    
        default Macchina fromId(Long id) {
            if (id == null) {
                return null;
            }
            Macchina macchina = new Macchina();
            macchina.setId(id);
            return macchina;
        }
    }
    
    @Generated(
        value = "org.mapstruct.ap.MappingProcessor",
        date = "2017-07-21T11:57:12+0200",
        comments = "version: 1.1.0.Final, compiler: Eclipse JDT (IDE) 3.12.3.v20170228-1205, environment: Java 1.8.0_141 (Oracle Corporation)"
    )
    @Component
    public class MacchinaMapperImpl implements MacchinaMapper {
    
        @Autowired
        private PersonaMapper personaMapper;
    
        @Override
        public MacchinaDTO toDto(Macchina arg0) {
            if ( arg0 == null ) {
                return null;
            }
    
            MacchinaDTO macchinaDTO = new MacchinaDTO();
    
            macchinaDTO.setId( arg0.getId() );
            macchinaDTO.setMarca( arg0.getMarca() );
            Set<PersonaDTO> set = personaSetToPersonaDTOSet( arg0.getPersonas() );
            if ( set != null ) {
                macchinaDTO.setPersonas( set );
            }
    
            return macchinaDTO;
        }
    
        @Override
        public List<MacchinaDTO> toDto(List<Macchina> arg0) {
            if ( arg0 == null ) {
                return null;
            }
    
            List<MacchinaDTO> list = new ArrayList<MacchinaDTO>();
            for ( Macchina macchina : arg0 ) {
                list.add( toDto( macchina ) );
            }
    
            return list;
        }
    
        @Override
        public Macchina toEntity(MacchinaDTO arg0) {
            if ( arg0 == null ) {
                return null;
            }
    
            Macchina macchina = new Macchina();
    
            macchina.setId( arg0.getId() );
            macchina.setMarca( arg0.getMarca() );
            Set<Persona> set = personaDTOSetToPersonaSet( arg0.getPersonas() );
            if ( set != null ) {
                macchina.setPersonas( set );
            }
    
            return macchina;
        }
    
        @Override
        public List<Macchina> toEntity(List<MacchinaDTO> arg0) {
            if ( arg0 == null ) {
                return null;
            }
    
            List<Macchina> list = new ArrayList<Macchina>();
            for ( MacchinaDTO macchinaDTO : arg0 ) {
                list.add( toEntity( macchinaDTO ) );
            }
    
            return list;
        }
    
        protected Set<PersonaDTO> personaSetToPersonaDTOSet(Set<Persona> set) {
            if ( set == null ) {
                return null;
            }
    
            Set<PersonaDTO> set_ = new HashSet<PersonaDTO>();
            for ( Persona persona : set ) {
                set_.add( personaMapper.toDto( persona ) );
            }
    
            return set_;
        }
    
        protected Set<Persona> personaDTOSetToPersonaSet(Set<PersonaDTO> set) {
            if ( set == null ) {
                return null;
            }
    
            Set<Persona> set_ = new HashSet<Persona>();
            for ( PersonaDTO personaDTO : set ) {
                set_.add( personaMapper.toEntity( personaDTO ) );
            }
    
            return set_;
        }
    }
    

    【讨论】:

    • I would like to try to avoid to add Macchina to every Persona by hand.,由于您没有将 Macchina 映射到 Persona,因此您必须手动进行。或者您也可以在此处将 Macchina 映射到 Persona personaDTOSetToPersonaSet\
    猜你喜欢
    • 2017-03-15
    • 2022-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多