Composite Key with JPA and Hibernate

Viewed 984

In this code, I'm trying generate a composite key with that model class, but when I execute the post method throw an exception.

I'm using postgres as database. Then I need the hibernate generate the "RA" and Idprojeto automatically.

Model Id class

   @Data
   @EqualsAndHashCode(onlyExplicitlyIncluded = true)
   @AllArgsConstructor
   @NoArgsConstructor
   class ProjetoId implements Serializable{
   Long idProjeto;
   Long ra;
  }

Model Entity

@Entity
@Data
@IdClass(ProjetoId.class)
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Table(name = "projeto", schema = "public")
public class Projeto {

@EqualsAndHashCode.Include
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id_projeto")
private Long idProjeto;

@Valid
@ConvertGroup(from = Default.class, to = ValidationGroups.ClienteId.class)
@NotNull
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "id_cliente")
private Cliente cliente;

@Valid
@NotNull
@ElementCollection
private List<ProdutoToProjeto> produtos = new ArrayList<>();

@EqualsAndHashCode.Include
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ra")
private Long ra;

@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@Enumerated(EnumType.STRING)
@Column(name = "situacao")
private StatusProjeto situacao;

@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@Column(name = "data_criacao")
private LocalDate dataRegistro;

public void finalizar() {
    if(naoPodeSerFinalizado()) {
        throw new ProjetoException("Projeto não pode ser finalizado");
    }
    setSituacao(StatusProjeto.APROVADO);
}

public void cancelar() {
    if(naoPodeSerFinalizado()) {
        throw new ProjetoException("Projeto não pode ser cancelado");
    }
    setSituacao(StatusProjeto.RECUSADO);
}

public boolean podeSerFinalizado() {
    return StatusProjeto.PEDENTE.equals(getSituacao());
}

public boolean naoPodeSerFinalizado() {
    return !podeSerFinalizado();
}

}

Error

   java.lang.IllegalArgumentException: Can not set java.lang.Long field com.ciasense.apiciasense.model.ProjetoId.ra to org.hibernate.id.IdentifierGeneratorHelper$2
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167) ~[na:na]
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171) ~[na:na]
    at java.base/jdk.internal.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81) ~[na:na]
    at java.base/java.lang.reflect.Field.set(Field.java:778) ~[na:na]
2 Answers

If you use DB SEQUENCE as the auto generation type only then composite key can work. Otherwise you have to write custom key generator and assign values to both the fields of your composite key before inserting.

@EqualsAndHashCode.Include
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "id_projeto")
private Long idProjeto;

@EqualsAndHashCode.Include
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "ra")
private Long ra;

If i understand, you want to do a key composite ? If it is, you have two thing to do :

1- annoted your class with @IdClass as you do and put @Id on properties you want to link with your key and Equals and HashCode methods (your IDE generate it easily). (this thing is already done for you)

2- In your repository, you have to define your key also like normal key example : (seems to not be done, it is the "ProjetoId" in the extends class)

@Repository
public interface ProjetoRepository extends JpaRepository<Projeto, ProjetoId> {
    List<Projeto> findByDataRegistro(Date date);
}

you can also go to see baeldung which explain it with more details than me.

https://www.baeldung.com/jpa-composite-primary-keys

Related