Spring 5 Data Jpa - OneToOne on a specific literal property

224 Views Asked by At

After Spring 5 Data Jpa - OneToOne, new entity, a single insert operation is solved,

I have the following tables:

  • UserDetails, with userId
  • UserPreference, with userId, propertyType, values, and isFavorite

I'm trying to map them so that each propertyType will be a single variable on UserDetails. I tried various annotations and ways, but I awlays end up with an issue; either on startup or when attempting to query the User (which will query the UserDetails, relevant in this question).

Following is the current setup I have, which makes Spring throw this error on startup:

Caused by: org.hibernate.MappingException: A '@JoinColumn' references a column named 'propertyType' but the target entity 'com.msh.UserPreference' has no property which maps to this column

Property type:

public enum PropertyType {
  Buffering,
  AntiAliasing,
  ScreenSize,
}

public enum BufferMode {
  None,
  Double,
  Triple,
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class UserDetails {
    @Id
    @Column(name = "user_id")
    private String userId;

    @OneToOne
    @MapsId
    @JoinColumn(name = "user_id")
    @ToString.Exclude
    private User user;

    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumnsOrFormulas({
            @JoinColumnOrFormula(formula = @JoinFormula(value = "'Buffering'", referencedColumnName = "propertyType")),
            @JoinColumnOrFormula(column = @JoinColumn(referencedColumnName = "userId"))
    })
    private UserPreference<BufferMode> buffering;

/* After:
    ...
    private UserPreference<Bool> antiAliasing;
*/

  public UserDetails(User user) {
    this.user = user;
    this.buffering = new UserPreference<>(user, PropertyType.Buffering);
  }
}

And UserPreference:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class UserPreference<T> {
    @Id
    @Column(name = "user_id")
    private String userId;

    @OneToOne
    @MapsId
    @JoinColumn(name = "user_id")
    @ToString.Exclude
    private User user;

    @Id
    private PropertyType propertyType;

    @Convert(converter = JsonConverter.class)
    @JavaType(value = StringJavaType.class)
    private List<T> values;

    private boolean favorite;

    public UserPreference(User user, PropertyType property) {
        this.user = user;
        this.propertyType = property;
    }
}
1

There are 1 best solutions below

1
TheTurTel500 On

the issue your having is with the mapping annotation i am guessing try making these changes User Details :

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class UserDetails {
    @Id
    @Column(name = "user_id")
    private String userId;

    @OneToOne(mappedBy = "userDetails", cascade = CascadeType.ALL, orphanRemoval = true)
    private UserPreference<BufferMode> buffering;


    public UserDetails(User user) {
        this.userId = user.getId(); // Assuming you have a getId() method in User entity
        this.buffering = new UserPreference<>(this, PropertyType.Buffering);
    }
}

UserPreference:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class UserPreference<T> {
    @Id
    @ManyToOne
    @JoinColumn(name = "user_id")
    @ToString.Exclude
    private UserDetails userDetails;

    @Enumerated(EnumType.STRING)
    private PropertyType propertyType;

    @Convert(converter = JsonConverter.class)
    @JavaType(value = StringJavaType.class)
    private List<T> values;

    private boolean favorite;

    public UserPreference(UserDetails userDetails, PropertyType property) {
        this.userDetails = userDetails;
        this.propertyType = property;
    }
}

This should help you achieve your desired mapping by using the mappedby annotation hopefully this helps you out