【问题标题】:Data is persisted but @JoinColumn = null数据被保留,但 @JoinColumn = null
【发布时间】:2019-08-28 10:32:02
【问题描述】:

我正在尝试执行简单的插入。有事件,它包含门票。当我只保存事件但 @JoinColumn 不生成 ID 时,事件和票证都被保存。它看起来像这样:

事件表

门票表:

谁能告诉我这里发生了什么?在我看来,映射是正确的。

基础实体:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@MappedSuperclass
public abstract class BaseEntity {

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

事件实体:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "Event")
@Table(name = "event")
public class Event extends BaseEntity {

    private String title;

    @Column(columnDefinition = "LONGTEXT")
    private String description;
    private LocalDate date;
    private String img;

    @Enumerated(EnumType.STRING)
    private MusicGenre genre;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "location_id")
    Location location;

    @OneToMany(
            cascade = CascadeType.ALL,
            mappedBy = "event")
    Set<Ticket> tickets = new HashSet<>();

    public void addTicket(Ticket ticket) {
        this.tickets.add(ticket);
        ticket.setEvent(this);
    }
}

票务实体:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "Ticket")
@Table(name = "ticket")
public class Ticket extends BaseEntity {

    @Enumerated(value = EnumType.STRING)
    private TicketType type;
    private Integer price;
    private Integer totalAmmount;
    private Integer inStock;

    @ManyToOne(
            cascade = {
                    CascadeType.DETACH,
                    CascadeType.MERGE,
                    CascadeType.PERSIST,
                    CascadeType.REFRESH
            }
    )
    @JoinColumn(name = "event_id")
    Event event;

}

请注意,我正在通过 Event 实体中的辅助方法在这些实体之间创建关联。

这就是我保存它们的方式:

@Override
@Transactional
public EventsDTO saveAll(EventsDTO eventsDTO) {

    return EventsDTO.builder()
            .events(
                    eventsDTO.getEvents().stream()
                            .map(eventDTO -> eventMapper.eventDTOtoEvent(eventDTO))
                            .peek(event -> event.getTickets().forEach(event::addTicket))
                            .map(eventRepository::save)
                            .map(event -> eventMapper.eventToEventDTO(event))
                            .collect(Collectors.toList())
            ).build();
}

这里是生成的脚本:

Hibernate: insert into event (date, description, genre, img, location_id, title) values (?, ?, ?, ?, ?, ?)
Hibernate: insert into ticket (event_id, in_stock, price, total_ammount, type) values (?, ?, ?, ?, ?)
Hibernate: insert into ticket (event_id, in_stock, price, total_ammount, type) values (?, ?, ?, ?, ?)
Hibernate: insert into ticket (event_id, in_stock, price, total_ammount, type) values (?, ?, ?, ?, ?)
Hibernate: insert into ticket (event_id, in_stock, price, total_ammount, type) values (?, ?, ?, ?, ?)
Hibernate: insert into ticket (event_id, in_stock, price, total_ammount, type) values (?, ?, ?, ?, ?)

和Mapper的实现:

@Override
public Event eventDTOtoEvent(EventDTO eventDTO) {
    if ( eventDTO == null ) {
        return null;
    }

    Event event = new Event();

    event.setId( eventDTO.getId() );
    event.setTitle( eventDTO.getTitle() );
    event.setDescription( eventDTO.getDescription() );
    event.setDate( eventDTO.getDate() );
    event.setImg( eventDTO.getImg() );
    event.setGenre( eventDTO.getGenre() );
    event.setLocation( locationDTOToLocation( eventDTO.getLocation() ) );
    event.setTickets( ticketDTOSetToTicketSet( eventDTO.getTickets() ) );

    return event;
}

   protected Set<Ticket> ticketDTOSetToTicketSet(Set<TicketDTO> set) {
        if ( set == null ) {
            return null;
        }

        Set<Ticket> set1 = new HashSet<Ticket>( Math.max( (int) ( set.size() / .75f ) + 1, 16 ) );
        for ( TicketDTO ticketDTO : set ) {
            set1.add( ticketDTOToTicket( ticketDTO ) );
        }

        return set1;
    }

   protected Ticket ticketDTOToTicket(TicketDTO ticketDTO) {
        if ( ticketDTO == null ) {
            return null;
        }

        Ticket ticket = new Ticket();

        ticket.setId( ticketDTO.getId() );
        ticket.setType( ticketDTO.getType() );
        ticket.setPrice( ticketDTO.getPrice() );
        ticket.setTotalAmmount( ticketDTO.getTotalAmmount() );
        ticket.setInStock( ticketDTO.getInStock() );
        ticket.setEvent( eventDTOtoEvent( ticketDTO.getEvent() ) );

        return ticket;
    }

【问题讨论】:

  • 不要使用仅用于记录内容的peek。相反,您应该在 y9our eventDTOtoEvent 方法中下降 addTicket
  • 为什么要使用 .peek(event -&gt; event.getTickets().forEach(event::addTicket)) 如果票都准备好绑定到事件,hibernate 会自动设置 id 事件到票
  • 你看过生成的SQL语句了吗?
  • 删除 peek 也不起作用,我添加了 sql 语句。
  • @MushifAliNawaz 添加了。

标签: sql spring hibernate spring-data-jpa


【解决方案1】:

您的代码存在两个问题。第一个在您的映射器中的ticketDTOToTicket() 方法中。在下面的语句中:

ticket.setEvent( eventDTOtoEvent( ticketDTO.getEvent() ) );

您正在为每个 Ticket 对象创建一个新的 Event 对象引用。这与持续存在的不同。每个 Ticket 对象应该引用相同的父 Event 对象引用。您应该将父 Event 对象引用存储在一个变量中,然后将相同的引用分配给所有 Ticket 对象。

第二个问题是以下行:

.peek(event -> event.getTickets().forEach(event::addTicket))

你在这里循环Set&lt;Ticket&gt; tickets并将它们添加回相同的HashSet&lt;&gt;()

public void addTicket(Ticket ticket) {
    this.tickets.add(ticket);
    ticket.setEvent(this);
}

这显然不会做任何事情,因为Set 不会添加重复的元素。您应该完全删除它。

判决:

您的代码的实际问题是每个Ticket 对象都不是 引用相同的父 Event 对象引用而不是每个 它们指的是如上所述的不同对象引用。修复此问题 将解决您的问题。

【讨论】:

  • You are creating a new Event object reference for each Ticket object. Which is different from the one which is being persisted. - 就 DB 而言,它是相同的对象,因为它们的 ID 相同。
  • @Vusal 如果他在event 变量中存储或传递父Event 对象的引用。他应该将相同的event 变量分配给所有Ticket 对象,如下所示:ticket.setEvent(event);。然后它将被正确持久化。
  • 关于您的声明:in terms of DB, it's the same objects, as their ID are the same.-Event 对象的 ID 尚未生成,它将在持久保存在数据库中时生成。由于没有一个Ticket 对象引用这个被持久化的Event 对象,这就是为什么event_id 被保存为NULL
  • 如果你有一个表 A 和 B,并且 A 是 B 的父级,所以 A 可以有多个 B,并且在你的代码中你可以这样写:` A a1 = new A() ; A a2 = 新 A(); a1.setId(1); a2.setId(1); B b1 = 新 B(); B b2 = 新 B(); b1.setA(a1); b2.setA(a2); ` - 那么在数据库中,b1b2 的两条记录将具有相同的父 ID,在本例中为 1。因此,您不必使用相同的 Java 对象作为父对象。你可以自己查看或者我可以提供最简单的spring app
  • 谁分配值并不重要。这个想法是 a1 和 a2 都具有相同的 id 值。如果 Java 对象的 id 相同,那么就 DB 而言,它是相同的记录。
【解决方案2】:

试试这些改变:

@Override
@Transactional
public EventsDTO saveAll(EventsDTO eventsDTO) {

    return EventsDTO.builder()
            .events(
                    eventsDTO.getEvents().stream()
                            .map(eventDTO -> eventMapper.eventDTOtoEvent(eventDTO))
                            .map(eventRepository::save)
                            .map(event -> eventMapper.eventToEventDTO(event))
                            .collect(Collectors.toList())
            ).build();
}

@Override
public Event eventDTOtoEvent(EventDTO eventDTO) {
    if ( eventDTO == null ) {
        return null;
    }

    Event event = new Event();

    event.setId( eventDTO.getId() );
    event.setTitle( eventDTO.getTitle() );
    event.setDescription( eventDTO.getDescription() );
    event.setDate( eventDTO.getDate() );
    event.setImg( eventDTO.getImg() );
    event.setGenre( eventDTO.getGenre() );
    event.setLocation( locationDTOToLocation( eventDTO.getLocation() ) );
    event.setTickets( ticketDTOSetToTicketSet( eventDTO.getTickets(), event ) );

    return event;
}

protected Set<Ticket> ticketDTOSetToTicketSet(Set<TicketDTO> dtos, Event event) {
    if ( dtos == null ) {
        return null;
    }

    return dtos.stream().map(d -> ticketDTOToTicket(d, event)).collect(Collectors.toSet());
}

protected Ticket ticketDTOToTicket(TicketDTO ticketDTO, Event event) {
    if ( ticketDTO == null ) {
    return null;
    }

    Ticket ticket = new Ticket();

    ticket.setId( ticketDTO.getId() );
    ticket.setType( ticketDTO.getType() );
    ticket.setPrice( ticketDTO.getPrice() );
    ticket.setTotalAmmount( ticketDTO.getTotalAmmount() );
    ticket.setInStock( ticketDTO.getInStock() );
    ticket.setEvent(event);

    return ticket;
}

仅供参考:最好从方法返回空集合而不是 null,返回类型为 Collection

【讨论】:

    猜你喜欢
    • 2016-10-15
    • 2015-07-16
    • 1970-01-01
    • 2013-09-02
    • 2016-01-30
    • 2017-05-29
    • 1970-01-01
    • 2020-12-30
    • 1970-01-01
    相关资源
    最近更新 更多