N-N relationship with additional fields (problems persisting data)

4

I have an N-N relationship that works as follows:

Destination.java (N) - (N) CustomerService.java

Within this relationship there is an entity that holds the Id's of relationships, which has some more values. The name of this is ServiceItem.java

Below the entity's:

Destination.java

@Entity
@Table(name="destination")
public class Destination implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id_destination")
    private Long idDestination;

    @Column(name="tenant_id", insertable=false, updatable=false)
    private Long tenantId;

    @Column(name="appear_website")
    private Boolean dtAppearWebsite;

    @Lob
    @Column(name="description")
    @NotEmpty(message="O campo \"Descrição do Destino\" não pode estar em branco.")
    private String dtDescription;

    @OneToMany(mappedBy="destination")
    private Set<ServiceItem> serviceItem;

    //Mais campos adicionais e Getters and Setters

    public Destination() {
    }
}

CustomerService.java

@Entity
@Table(name = "customer_service")
public class CustomerService implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_customer_service")
    private Long id;

    @Column(name = "tenant_id", insertable = false, updatable = false)
    private Long tenantId;

    @Column(name = "date_service")
    @DateTimeFormat(pattern = "dd/MM/yyyy")
    @Temporal(TemporalType.DATE)
    private Date date;

    @Column(name = "average_budget")
    private BigDecimal averageBudget;

    @Column(name = "service_situation")
    private boolean situation;

    @OneToMany(cascade = CascadeType.PERSIST, mappedBy = "customerService")
    private List<ServiceItem> serviceItem;

    @OneToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "fk_history")
    private History history;

    @Column(name = "service_observatons")
    private String serviceObservations;

    public CustomerService() {

    }

    //Mais campos adicionais e Getters and Setters

}

ServiceItem.java

@Entity
@Table(name = "service_item")
@IdClass(ServiceItemId.class)
public class ServiceItem {

    @Id
    @ManyToOne
    @JoinColumn(name="destination_id")
    private Destination destination;

    @Id
    @ManyToOne
    @JoinColumn(name="customerService_id")
    private CustomerService customerService;

    @Column(name="tenant_id", insertable=false, updatable=false)
    private Long tenantId;   

    @Column(name="value_negotiated")
    private double valueNegotiated;

    @Enumerated(EnumType.STRING)
    @Column(name="sale_type")
    private SaleType saleType;

    @Column(name="departure_date")
    @DateTimeFormat(pattern="dd/MM/yyyy")
    @Temporal(TemporalType.DATE)
    private Date departureDate;

    @Column(name="arrival_date")
    @DateTimeFormat(pattern="dd/MM/yyyy")
    @Temporal(TemporalType.DATE)
    private Date arrivalDate;

    @Column(name="requested_destination")
    private Boolean requestedDestination;

    @Column(name="negociation_observations")
    private String negociationObservations;

    public ServiceItem() {

    }

    //Getters and Setters
}

To generate the id's there is the class ServiceItemId.java

public class ServiceItemId implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long destination;
    private Long customerService;

    public Long getCustomerService() {
        return customerService;
    }
    public void setCustomerService(Long customerService) {
        this.customerService = customerService;
    }
    public Long getDestination() {
        return destination;
    }
    public void setDestination(Long destination) {
        this.destination = destination;
    }

    @Override
    public int hashCode() {
        return (int) (destination + customerService);
    }

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof ServiceItemId){
            ServiceItemId serviceItemId = (ServiceItemId) obj;
            return serviceItemId.destination == destination && serviceItemId.customerService == customerService;
        }

        return false;
    }

}

Well, the question is when will I persist the data ...

Dez 17, 2014 4:52:27 PM org.apache.catalina.core.StandardWrapperValve invoke
GRAVE: Servlet.service() for servlet [appServlet] in context with path [/viatge] threw exception [Request processing failed; nested exception is org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20131113-a7346c6): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'customerService_id' cannot be null
Error Code: 1048
Call: INSERT INTO service_item (arrival_date, departure_date, negociation_observations, requested_destination, sale_type, value_negotiated, customerService_id, destination_id, tenant_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
    bind => [2016-08-01, 2015-10-01, teste, true, SUBMITTED_BUDGET, 3658.98, null, 1, 2]
Query: InsertObjectQuery(br.com.joocebox.model.ServiceItem@6e92b1b1)] with root cause
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'customerService_id' cannot be null
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
    at com.mysql.jdbc.Util.getInstance(Util.java:386)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1041)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4187)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4119)

How could you proceed in such a case?

EDITION

Come on!

My entity CustomerService (which refers to a service) has many destinations (in this case Destination ), as many destinations can belong to many CustomerService.java ).

Note that there is no @ManyToMany relationship directly between entities, but there is a class called ServiceItem that couples between the two entities.

When I persist a ServiceItem, my id for Destination is loaded, (since it already exists), the CustomerService has not yet been persisted, so it will not have an ID what is causing the problem.

    
asked by anonymous 17.12.2014 / 21:47

1 answer

1

Responding to the discussion in the comments: Both UUID (GUID) and ID use have their pros and cons.

The more technical details I leave for the original post, which answer is based:

SOen - advantages-and-disadvantages-of-guid-uuid -database-keys

SOen-how-do-you-like-your-primary-keys

SOen-generate-uuid-in-java

Here's a simple example, note that @PrePersist will be executed by your framework ORM. More details about entity listener HERE .

@Entity
@Table(name = "service_item")
public class ServiceItem {

    @Id
    //Long ? Integer ?
    private String uuid;

    @ManyToOne
    @JoinColumn(name="destination_id")
    private Destination destination;

    @ManyToOne
    @JoinColumn(name="customerService_id")
    private CustomerService customerService;


    @PrePersist
    public void init() {
        //Aqui voce gera seu GUID
        //Note que existem varios outros meios
        //Estou utilizando esse pois é nativo e pratico
        this.uuid = UUID.randomUUID().toString();
    }

    //...
}
    
18.12.2014 / 04:08