Search code examples
jpaspring-bootspring-data-jpaspring-data-rest

JPA2.0 property access in spring rest data -- some getters not being called


I am still somewhat of a novice with Spring Boot and Spring Data Rest and hope someone out there with experience in Accessing by Property. Since I cannot change a database which stores types for Letters in an unnormalized fashion (delimited string in a varchar), I thought that I could leverage some logic in properties to overcome this. However I notice that when using property access, some of my getters are never called.

My Model code:

package ...
import ...

@Entity
@Table(name="letters", catalog="clovisdb")
@Access(AccessType.PROPERTY)
public class Letter {

  public enum PhoneticType {
    VOWEL, SHORT, LONG, COMMON;
    public static boolean contains(String s) { ... }
  }
  public enum PositionType {
    ALL, INITIAL, MEDIAL, FINAL;
    public static boolean contains(String s) { ... }
  }
  public enum CaseType {
    ALL, LOWER, UPPER;
    public static boolean contains(String s) { ... }
  }

  private int id;
  private String name;
  private String translit;
  private String present;
  private List<PhoneticType> phoneticTypes;
  private CaseType caseType;
  private PositionType positionType;

  @Id
  public int getId() { return id; }
  public void setId(int id) { this.id = id; }

  public String getName() { return name; }
  public void setName(String name) { this.name = name; }

  public String getTranslit() { return translit; }
  public void setTranslit(String translit) { this.translit = translit; }

  public String getPresent() { return present; }
  public void setPresent(String present) { this.present = present; }

  public String getTypes() { 
    StringBuilder sb = new StringBuilder(); //
    if (phoneticTypes!=null) for (PhoneticType type : phoneticTypes) sb.append(" ").append(type.name());
    if (caseType!=null) sb.append(" ").append(caseType.name());
    if (positionType!=null) sb.append(" ").append(positionType.name());
    return sb.substring( sb.length()>0?1:0 );
  }
  public void setTypes(String types) {
    List<PhoneticType> phoneticTypes = new ArrayList<PhoneticType>();
    CaseType caseType = null;
    PositionType positionType = null;
    for (String val : Arrays.asList(types.split(" "))) {
        String canonicalVal = val.toUpperCase();
        if (PhoneticType.contains(canonicalVal)) phoneticTypes.add(PhoneticType.valueOf(canonicalVal));
        else if (CaseType.contains(canonicalVal)) caseType = CaseType.valueOf(canonicalVal);
        else if (PositionType.contains(canonicalVal)) positionType = PositionType.valueOf(canonicalVal);
    }
    this.phoneticTypes = phoneticTypes; 
    this.caseType = (caseType==null)? CaseType.ALL : caseType;
    this.positionType = (positionType==null)? PositionType.ALL : positionType;
  }

  @Override
  public String toString() {  ....  }

}

My Repository/DAO code:

package ...
import ...

@RepositoryRestResource
public interface LetterRepository extends CrudRepository<Letter, Integer> {
  List<Letter> findByTypesLike(@Param("types") String types);
}

Hitting this URI: http://mytestserver.com:8080/greekLetters/6 and setting breakpoints on all the getters and setters, I can see that the properties are called in this order:

setId
setName
setPresent
setTranslit
setTypes
 (getId not called)
getName
getTranslit
getPresent
 (getTypes not called !!)

The json returned for the URI above reflects all the getters called, and there are no errors

{
 "name" : "alpha",
 "translit" : "`A/",
 "present" : "Ἄ",
 "_links" : {
  "self" : {
    "href" : "http://mytestserver.com:8080/letters/6"
  }
 }
}

But why is my getTypes() not being called and my JSON object missing the “types” attribute? I note that the setter is called, which makes it even stranger to me.

Any help would be appreciated!

Thanks in advance


Solution

  • That's probably because you don't have a field types, so getTypes() isn't a proper getter. Try adding this to your entity

    @Transient
    private String types;
    

    I don't know how the inner works, but it's possible that the class is first scanned for its fields, and then a getter is called for each field. And since you don't have types field, the getter isn't called. Setter getting called could be a feature but I wouldn't be surprised if it is a bug, because findByTypesLike should translate to find Letters whose types field is like <parameter>, and types is not a field.

    Another thing you can try, is to annotate that getter with @JsonInclude. Jackson 2 annotations are supported in Spring versions 3.2+ (also backported to 3.1.2).