7

I have a Spring repository as follows:

import org.springframework.data.repository.Repository;
import org.springframework.stereotype.Component;

import com.test.domain.My;

@Component
public interface MyRepository extends Repository<My, String> {

    My findOne(String code);

    My findByName(String name);

}

The entity class is:

import javax.persistence.ColumnResult;
import javax.persistence.ConstructorResult;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown=true)
@Entity
@Table(name="vMy", schema="test")
@SqlResultSetMapping(
    name="something",
    classes = {
        @ConstructorResult(targetClass = My.class,
            columns={
                @ColumnResult(name = "myCode", type = String.class),
                @ColumnResult(name = "myShortName", type = String.class)
             }
        )
    }
)
public class My {

    @Id
    @Column(name = "myCode")
    private final String code;

    @Column(name = "myShortName")
    private final String name;

   public My(String code, String name) {
        this.code = code;
        this.name = name;
   }

   @JsonCreator()
   public My(@JsonProperty("My_c") String code) {
       this.code = code;
       this.name = null;
  }

  public String getCode() {
      return code;
  }

  public String getName() {
      return name;
  }

  @Override
    public String toString() {
       return "{code: " + code + ", name: " + name + "}";
   }    
 } 

When findOne or findByName is invoked, the following error is given:

   org.hibernate.InstantiationException: No default constructor for entity 

How can I use Spring JPA repository and not have a default constructor? I would like to keep the instance fields, code and name, final.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
James
  • 2,876
  • 18
  • 72
  • 116
  • 1
    Your Entity does not have **any** constructor, unless the `public Portfolio` is supposed to be the constructor. And why do you need the fields of your Entity to be `final`? I have never seen that in any Entity. – DuncanKinnear Jun 23 '15 at 02:29
  • Yes, public Portfolio is supposed to be the constructor. I changed it to public My. I would prefer the fields of the Entity to be final so that once the entity is created it's state cannot be changed (i.e. immutable). – James Jun 23 '15 at 14:53
  • I think you might be mixing up the role of Entity and DTO (Data Transfer Object). While EJB3 allowed Entities to be de-coupled from the database layer, which was a big improvement over EJB2, the role of DTOs is still a valid one in EJB3. Your final fields are more at place in a DTO rather than an Entity. If you make these Entity fields final, how on earth are you going to modify these Entities. While it may not be a requirement now, who knows what your application needs to do in the future. I would not put such a big limitation on my Entities and it's fields. – DuncanKinnear Jun 23 '15 at 19:58
  • That makes sense. It is an entity corresponding to a view in a third party database. The application has read only access to the database. It should never be able to write. – James Jun 23 '15 at 22:31
  • So, given that instances of this class should never have state modified after creation, how can I accomplish this? – James Jun 24 '15 at 19:29
  • So what if the state can be modified? The connection to the database is read-only, so you're never going to change the database. What exactly are you worried about? If you are writing the software, just make sure you never call any setters on the Entity. How hard can that be, or am I missing something? – DuncanKinnear Jun 24 '15 at 20:59
  • 1
    Maintainability. If others use the class, the public API will indicate that the class is mutable (when it really should not be). Also, it's not immediately evident that other parts of the code never changes its state after object creation. Yes, I could comment the code to document that. But comments don't actually enforce it. I don't think we would say let's make the instance fields public because I know that I would never directly access them in other parts of the code. – James Jun 25 '15 at 16:18
  • Having said that, with private fields (that aren't `final`) marked with `@Column` and no public setters for those fields, then your `My` class would still be publicly immutable (except via reflection). However, the DTO way seems to be more industry standard. At some point you will find that you need Hibernate-specific annotation, and if you want to pass your Entities up into your presentation layer, that will mean that your presentation layer needs to know about Hibernate. Not so bad for web stuff, but we use Swing clients, so we use DTOs throughout. – DuncanKinnear Jun 30 '15 at 21:51

1 Answers1

6

I would create a separate class called MyDto which has the JSON stuff, but not the Entity annotations. Make it's fields final as well.

Then your repository methods would be something like this:

@Query("SELECT new MyDto(m.code, m.name) FROM My m WHERE m.code = :code")
public MyDto findByCode(@Param("code") String code);

That way, you are only using the My Entity class to give you the mapping to the database columns, not creating an instance of My.


EDIT: Another approach (as detailed here) is to use the Entity class itself as the DTO.

So your query method could look like this:

@Query("SELECT new My(m.code, m.name) FROM My m WHERE m.code = :code")
public My findByCode(@Param("code") String code);

This has the advantage of not having to create a separate DTO class.

DuncanKinnear
  • 4,563
  • 2
  • 34
  • 65
  • 1
    I'm not sure that I follow. Did you mean to have the repository method (findByCode) returning a MyDto instance? If so, how would My look? – James Jun 25 '15 at 22:29
  • Yes, the method will return a DTO not an Entity. The `My` class can be exactly the way you have it now. It doesn't actually matter as it is only being used to define the ORM (Object-Relational Mapping). You're never going to instantiate an instance of `My`. – DuncanKinnear Jun 28 '15 at 21:03
  • 1
    The JPA 2.1 specification states "The entity class must have a no-arg constructor" & "No methods or persistent instance variables of the entity class may be final." I've noticed that some imply immutability by providing a protected no-arg constructor & no setters (e.g. [Spring Data example](https://github.com/spring-projects/spring-data-book/blob/1db59b77bea7139ee54a0bdb62c38e9cfc82c432/jpa/src/main/java/com/oreilly/springdata/jpa/core/Address.java). However, your answer actually allows the entity to be defined without a no-arg constructor and all final fields. (+1). – James Jun 30 '15 at 15:54
  • Is that b/c it only uses the entity maps columns to fields (i.e. it doesn't create entity instances)? – James Jun 30 '15 at 15:57
  • Yes, only in my second example is Hibernate constructing `My` instances, but only as POJOs, so the JPA 'rules' don't apply. The DTO way was how you **had** to do it with EJB2. – DuncanKinnear Jun 30 '15 at 21:54