jpaeclipselinkpersistencesingle-table-inheritancediscriminator

When using discriminator value for single table strategy, the first inserted entity's discriminator value is null but the value is there in database


When using discriminator value for inheritance/single table strategy, the first inserted entity's discriminator value is null but the value is there in the database. I have to restart the server so that the query result containes the discriminator value:

package entity;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.MappedSuperclass;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;


@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="user_type", discriminatorType = DiscriminatorType.STRING)
@DiscriminatorValue("Null")
@Table(name="ALLUSER")
@NamedQueries({
    @NamedQuery(name = "User.findAll", query = "SELECT u FROM User u"),
    @NamedQuery(name = "User.findByAccount", query = "SELECT u FROM User u WHERE u.account = :account")
})
public class User implements Serializable{
    
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String account;
    private String password;
    private String userType;
    
    public User() {
        super();
    }
    public User(String account, String password) {
        super();
        this.account = account;
        this.password = password;
    }
    
    @Id
    @Column(name = "account")
    public String getAccount() {
        return account;
    }
    public void setAccount(String account) {
        this.account = account;
    }
    
    @Column(name = "password")
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    
    @Column(name = "user_type", insertable = false, updatable = false, nullable = false)
    public String getUserType() {
        return userType;
    }
    public void setUserType(String userType) {
        this.userType = userType;
    }
    
    @Override
    public String toString() {
        return account;
    }
    
    

}
@Entity
@DiscriminatorValue("Normal")
@NamedQueries({
    @NamedQuery(name = "NormalUser.findAll", query = "SELECT u FROM NormalUser u")
})
public class NormalUser extends User implements Serializable{
    
    /**
     * 
     */
    //private String account;
    private static final long serialVersionUID = 1L;
    private LinkedHashSet<Customer> customers;
    
    public NormalUser() {
        super();
    }
    

    @OneToMany(fetch=FetchType.EAGER, mappedBy="normalUser", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}) //eager can be optimized when deleting a normal user
    public LinkedHashSet<Customer> getCustomers() {
        return customers;
    }

    public void setCustomers(LinkedHashSet<Customer> customers) {
        this.customers = customers;
    }
    
//  @Column(name = "account")
//  //have to override in order to get account to use
//  public String getAccount() {
//      return account;
//  }
//  
//  public void setAccount(String account) {
//      this.account = account;
//  }
}

If I just add a new normal user(child entity), then query this user whose user type will be null:

result null

I use eclipse-link as the JPA implementation and Java EE three-tiered web architecture. I know this definitely has something to do with the working of entity manager and persistence but I don't know the details. I also don't know how to resolve it. Any suggestion is welcome!


Solution

  • You are not setting the 'type' field within your entities, and JPA doesn't set it for you - not in the java object anyway. If it isn't set when you persist an entity, it will remain unset for as long as that entity is cached (locally or the shared EMF level cache). Restarting the app works because it clears the cache, forcing any fetches of existing entities to load from the database, where the type was set based on the discriminator column value.

    You can set the type when creating the class, or force the data to be reloaded from the database by calling em.refresh on the instance. In this case though, it seems strange to even bother mapping the type column as a basic mapping - the getType method should just return the static discriminator value for the class, and you cannot change the type string anyway.