4

I have found a lot of questions on SO with this error message but unfortunately (fortunately for people who previously had this issue) they all were missing the column they intended to map. I have two classes ResetPin and User and would like to establish OneToOne mapping between ResetPin and User.email

But whenever I run my project I get the following error message:

Caused by: org.hibernate.MappingException: Unable to find column with logical name: email in org.hibernate.mapping.Table(users) and its related supertables and secondary tables
    at org.hibernate.cfg.Ejb3JoinColumn.checkReferencedColumnsType(Ejb3JoinColumn.java:582)
    at org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:258)
    at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:116)
    at org.hibernate.cfg.Configuration.processEndOfQueue(Configuration.java:1598)
    at org.hibernate.cfg.Configuration.processFkSecondPassInOrder(Configuration.java:1521)
    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1422)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:852)

Below are my classes:

ResetPin class:

public class ResetPin implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @OneToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "email", referencedColumnName = "email")
    private User user;

    @NotNull
    @Size(min = 5, max = 50)
    @Column(name = "token")
    private String token;

    @Column(name = "active")
    private Boolean active;

    //Getters and setters
    .
    .
    .
    .
}

User class:

public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

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

    @NotNull
    @Size(min = 5, max = 50)
    @Column(name = "email", unique = true)
    private String email;

    @NotNull
    @Size(min = 5, max = 50)
    @Column(name = "password")
    private String password;

    //Getters and setters
    .
    .
    .
    .

}
Nick Div
  • 5,338
  • 12
  • 65
  • 127
  • A relation does not join on a non-PK column. – Neil Stockton Aug 27 '17 at 17:15
  • Yes it does with certain implementations like EclipseLink and Hibernate. https://stackoverflow.com/questions/5855637/jpa-providers-why-do-relationships-fks-to-non-pk-columns-work-in-hibernate-and – Nick Div Aug 27 '17 at 17:30

2 Answers2

3

referencedColumnName refers to the name of the column representing the association in the other table (in your case User) and it should map to the primary key

  @JoinColumn(name = "email", referencedColumnName = "id")

It is by default referencing to the primary key unless you have a composite key then you need to use it, so in your case its enough like this

@JoinColumn(name = "email")
Amer Qarabsa
  • 6,412
  • 3
  • 20
  • 43
  • But I would like the reference column to be email, which is a unique key in table users – Nick Div Aug 27 '17 at 16:55
  • @NickDiv and why do you need this? – Amer Qarabsa Aug 27 '17 at 16:55
  • Because there are going to be many calls to ResetPin table looking up by email address. – Nick Div Aug 27 '17 at 16:56
  • 1
    @NickDiv in this case you will have to retrieve the relevant emails urself since hibernate (as far as I know ) does not implement this feature – Amer Qarabsa Aug 27 '17 at 17:00
  • I have seen it working in a different project but unfortunately I do not have access to it anymore. Also, for anyone else who might come across this issue, this solution does work with primary key. But I already had that working :( I need to map email with the reset pin – Nick Div Aug 27 '17 at 17:09
0

Its working for me. In my case I have entities appUser and screen. However I have implemented it slightly differently using unique constraint on entity instead of inside column annotation as follows:

Entity AppUser:

@Entity
@Table(
        name="app_user",
        schema="public",
        uniqueConstraints={
                @UniqueConstraint(columnNames={"username"}),
                @UniqueConstraint(columnNames={"email_id"}),
                @UniqueConstraint(columnNames={"screen_name"}),
        }
)
public class AppUser {
    Integer appUserId;
    String firstName;
    String lastName;
    String username;
    String password;
    String emailId;
    String phoneNumber;
    City city;
    Industry industry;
    String companyName;
    Set<Authority> authorities = new HashSet<>(0);
    Boolean active;
    String emailConfirmationToken;
    Screen screen;

    public AppUser() {
        super();
    }

    public AppUser(Integer appUserId, String firstName, String lastName, String username, String password,
            String emailId, String phoneNumber, City city, Industry industry, String companyName,
            Set<Authority> authorities, Boolean active, String emailConfirmationToken,
            Screen screen) {
        super();
        this.appUserId = appUserId;
        this.firstName = firstName;
        this.lastName = lastName;
        this.username = username;
        this.password = password;
        this.emailId = emailId;
        this.phoneNumber = phoneNumber;
        this.city = city;
        this.industry = industry;
        this.companyName = companyName;
        this.authorities = authorities;
        this.active = active;
        this.emailConfirmationToken = emailConfirmationToken;
        this.screen = screen;
    }

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="app_user_id")
    public Integer getAppUserId() {
        return appUserId;
    }
    public void setAppUserId(Integer appUserId) {
        this.appUserId = appUserId;
    }

    @Column(name="first_name")
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    @Column(name="last_name")
    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Column(name="username")
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }

    @Column(name="password")
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }

    @Column(name="email_id")
    public String getEmailId() {
        return emailId;
    }
    public void setEmailId(String emailId) {
        this.emailId = emailId;
    }

    @Column(name="phone_number")
    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    @ManyToOne
    @JoinColumn(name="city_id")
    public City getCity() {
        return city;
    }


    public void setCity(City city) {
        this.city = city;
    }

    @ManyToOne
    @JoinColumn(name="industry_id")
    public Industry getIndustry() {
        return industry;
    }


    public void setIndustry(Industry industry) {
        this.industry = industry;
    }

    @Column(name="company_name")
    public String getCompanyName() {
        return companyName;
    }


    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }



    @ManyToMany
    @JoinTable(
            name="app_user_authority",
            joinColumns={@JoinColumn(name="app_user_id",nullable=false,updatable=false)},
            inverseJoinColumns={@JoinColumn(name="authority_id",nullable=false,updatable=false)}
    )
    public Set<Authority> getAuthorities() {
        return authorities;
    }

    public void setAuthorities(Set<Authority> authorities) {
        this.authorities = authorities;
    }

    @Column(name="active")
    public Boolean getActive() {
        return active;
    }

    public void setActive(Boolean active) {
        this.active = active;
    }

    @Column(name="email_confirmation_token")
    public String getEmailConfirmationToken() {
        return emailConfirmationToken;
    }

    public void setEmailConfirmationToken(String emailConfirmationToken) {
        this.emailConfirmationToken = emailConfirmationToken;
    }

    @OneToOne
    @JoinColumn(name="screen_name",referencedColumnName="screen_name")
    public Screen getScreen() {
        return screen;
    }

    public void setScreen(Screen screen) {
        this.screen = screen;
    }



}

Entity Screen:

@Entity
@Table(name="screen",schema="public",
        uniqueConstraints={@UniqueConstraint(columnNames="screen_name")})

@JsonIgnoreProperties(ignoreUnknown=true)
public class Screen extends OEntity<Screen> implements Serializable{

    Integer screenId;
    String screenName;
    String address;
    ScreenType screenType;
    ScreenSize screenSize;
    BigDecimal latitude;
    BigDecimal longitude;

    @SecureUpdate({"ROLE_ADMIN"})
    Boolean active;

    public Screen() {
        super();
    }


    public Screen(Integer screenId, String screenName, String address, ScreenType screenType, ScreenSize screenSize,
            BigDecimal latitude, BigDecimal longitude, Boolean active) {
        super();
        this.screenId = screenId;
        this.screenName = screenName;
        this.address = address;
        this.screenType = screenType;
        this.screenSize = screenSize;
        this.latitude = latitude;
        this.longitude = longitude;
        this.active = active;
    }


    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="screen_id")
    public Integer getScreenId() {
        return screenId;
    }
    public void setScreenId(Integer screenId) {
        this.screenId = screenId;
    }

    @Column(name="screen_name")
    public String getScreenName() {
        return screenName;
    }
    public void setScreenName(String screenName) {
        this.screenName = screenName;
    }

    @Column(name="address")
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }

    @ManyToOne
    @JoinColumn(name="screen_type_id")
    public ScreenType getScreenType() {
        return screenType;
    }
    public void setScreenType(ScreenType screenType) {
        this.screenType = screenType;
    }

    @ManyToOne
    @JoinColumn(name="screen_size_id")
    public ScreenSize getScreenSize() {
        return screenSize;
    }
    public void setScreenSize(ScreenSize screenSize) {
        this.screenSize = screenSize;
    }

    @Column(name="latitude",precision=12,scale=9)
    public BigDecimal getLatitude() {
        return latitude;
    }
    public void setLatitude(BigDecimal latitude) {
        this.latitude = latitude;
    }

    @Column(name="longitude",precision=12,scale=9)
    public BigDecimal getLongitude() {
        return longitude;
    }
    public void setLongitude(BigDecimal longitude) {
        this.longitude = longitude;
    }

    @Column(name="active")
    public Boolean getActive() {
        return active;
    }

    public void setActive(Boolean active) {
        this.active = active;
    }
}

Also I had to implement Serializable on Screen.

ArslanAnjum
  • 1,674
  • 2
  • 17
  • 31
  • I tried that too, but still doesnt work. May I ask what implementation of JPA are you using? Is it Eclipse link – Nick Div Aug 27 '17 at 20:00
  • Its spring data jpa and its a clone of this project. https://github.com/ArslanAnjum/angularSpringApi. I modified it to test your requirement. – ArslanAnjum Aug 27 '17 at 20:12
  • Its using Hibernate. Can you post the complete Entities: Screen and AppUser? – Nick Div Aug 27 '17 at 20:20
  • try checking uniqueness and foreign key constraints in db. may be something is missing there. – ArslanAnjum Aug 27 '17 at 20:33
  • is it working if you dont use OneToOne altogether.? Also check if the corresponding class file gets updated after some changes. may be its an IDE bug, try cleaning the project ! – ArslanAnjum Aug 27 '17 at 20:47
  • The file definitely gets updated because as soon as I change it back to mapping ID column it starts working. Also, I havent tried any other mapping as its not the relationship between the tables and your example proves that it is possible. Your project on github is using almost the same pom that I am. So our library classes are the same including the version. – Nick Div Aug 27 '17 at 20:54
  • well you can also try debugging from where the error is originating. In org.hibernate.cfg.Ejb3JoinColumn.checkReferencedColumnsType – ArslanAnjum Aug 27 '17 at 21:01
  • have you checked import statement of User? Havent you by any chance imported User object from Spring Security? – ArslanAnjum Aug 27 '17 at 21:05
  • I did suspect User being a different class but I have the correct import. And I can try debugging. Will let you know what comes out. – Nick Div Aug 27 '17 at 21:50