Question

I'm rewriting some messy code that manages a database, and saw that the original programmer created a class mapped to the database like so:

(I've removed unnecessary code that has no purpose in this question)

@Entity
@Data
@EqualsAndHashCode(callSuper = false, of = { "accessionCode", "header", "date" })
@SuppressWarnings("PMD.UnusedPrivateField")
public class PDBEntry implements Serializable {
    @Id
    @NaturalId
    @NotEmpty
    @Length(max = 4)
    private String accessionCode;

    @NaturalId
    @NotEmpty
    private Date date;

    @NaturalId
    // We allow for the header to be 'null'
    private String header;

    private Boolean isValidDssp;

    @Temporal(TemporalType.TIMESTAMP)
    private Date lastUpdated = new Date(System.currentTimeMillis());

    protected PDBEntry(){}

    public PDBEntry(String accessionCode, String header, Date date){
        this.accessionCode = accessionCode;
        this.header = header;
        this.date = date;
    }
}

I am still a beginner at Hibernate and using Lombok, but wouldn't this do the same thing and wouldn't Lombok automatically create the needed constructor for you?

@Entity
@Data
@SuppressWarnings("PMD.UnusedPrivateField")
public class PDBEntry implements Serializable {
    @Id
    @NaturalId
    @NotEmpty
    @NonNull
    @Length(max = 4)
    private String accessionCode;

    @NaturalId
    @NotEmpty
    @NonNull
    private Date date;

    @NaturalId
    // We allow for the header to be 'null'
    private String header;

    private Boolean isValidDssp;

    @Temporal(TemporalType.TIMESTAMP)
    private Date lastUpdated = new Date(System.currentTimeMillis());
}

Also, the original programmer of this code says he allows for the header to be 'null', yet he explicitly created a constructor that needs a value for header. Am I missing something or is this a bit contradictory?

Was it helpful?

Solution

Have a look at @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor.

The constructor behavior of @Data is like @RequiredArgsConstructor:

@RequiredArgsConstructor generates a constructor with 1 parameter for each field that requires special handling. All final fields get a parameter, as well as any fields that are marked as @NonNull that aren't initialized where they are declared.

Given that none of your fields are either final or @NonNull, this will result in a no-argument constructor. However, this is not the most expressive way to achieve this behavior.

What you'll probably want in this case is a @NoArgsConstructor (optionally combined with a @AllArgsConstructor), to clearly communicate the intended behavior, as is also indicated in the documentation:

Certain java constructs, such as hibernate and the Service Provider Interface require a no-args constructor. This annotation is useful primarily in combination with either @Data or one of the other constructor generating annotations.

OTHER TIPS

That bit is contradictory you're right. I've not used Lombok before but with hibernate if you want to be able to create a bean and persist you need the default constructor as given above as far I was aware. It uses Constructor.newInstance() to instantiate new objects.

Here is some hibernate documentation which goes into more detail.

Hibernate Documentation

If you are using @Data with a @NonNull field and still want a noargs-constructor, you might wanna try to add all 3 annotation together

@NoArgsConstructor
@RequiredArgsConstructor
@AllArgsConstructor

Apparently an old intelliJ bug which I did replicate in Eclipse Kepler and lombok v0.11.4

@NoArgsConstructor, 
@RequiredArgsConstructor, 
@AllArgsConstructor

Generate constructors that take no arguments, one argument per final / non-null field, or one argument for every field. Read this lombok-project

@Data
@RequiredArgsConstructor /*Duplicate method Someclass() in type Someclass*/
@NoArgsConstructor(access=AccessLevel.PRIVATE, force=true)  /*Duplicate method Someclass() in type Someclass*/
@Entity
public class Someclass {      
    @Id
    private  String id;
    private  String name;
    private  Type type; 

    public static enum Type { X , Y, Z}
}

Fixed it by making member variables final

@Data
@RequiredArgsConstructor 
@NoArgsConstructor(access=AccessLevel.PRIVATE, force=true)
@Entity
public class Someclass {

    @Id
    private final String id;
    private final String name;
    private final Type type; 
    public static enum Type { X , Y, Z}
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top