简体   繁体   中英

JPA: Persist an Object which references a collection of an abstract class

I am trying to use JPA to persist a User object and having problems. The User references a set of Role objects which is an abstract class which will have multiple subclasses. At the moment, I just have one - Admin.

Here is the User class:

@Entity(name = "USERS")
public class User implements Serializable{

    @Id
    @Column(name = "USERNAME")
    private String username;

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

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

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

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "ROLE_ID")
    private Set<Role> roles;

    public User(){
        roles = new HashSet<Role>();
    }

    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Set<Role> getRoles(){
        return roles;
    }

    public void setRoles(Set<Role> roles){
        this.roles = roles;
    }

    public void addRole(Role role){
        roles.add(role);
    }
}

Here is Role:

@Entity(name = "ROLES")
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class Role implements Serializable{

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name = "ROLE_ID")
    int id;


    public void setId(int id){
        this.id = id;
    }

    public int getId(){
        return id;
    }
}

And here is Admin (empty for now but will be adding things to it later).

@Entity(name = "ADMINS")
public class Admin extends Role{

}

Now here's the code where I'm creating the objects and trying to persist them. I want to be able to persist a User object and have it's Role objects persisted automatically - I believe the CascaseType.ALL in User does this.

public class JPAManager {

    public void persist(Serializable s){
        EntityManagerFactory emf = getEntityManagerFactory();
        EntityManager em = emf.createEntityManager();
        EntityTransaction et = em.getTransaction();

        et.begin();
        em.persist(s);
        et.commit();
        em.close();
        emf.close();
    }


    protected EntityManagerFactory getEntityManagerFactory(){
        return Persistence.createEntityManagerFactory("main");
    }

}

Finally, here is the main method:

public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("context.xml");     

        User user = new User();
        user.setUsername("Chris842");
        user.setPassword("123456");
        user.setFirstName("Chris");
        user.setLastName("Jones");      
        user.addRole(new Admin());

        UserDAO dao = context.getBean("userDAO", UserDAO.class);
        dao.persist(user);
        System.out.println("Done");
    }

EDIT: After the first answer, I tried this in the main method but get the same exception:

        Admin admin = new Admin();
        admin.setId(1);
        user.addRole(admin);

When running the above, two tables are getting created in my database called USERS, ROLES and ADMINS. There seems to be no link between USERS and ROLES/ADMINS. I expected there to be a ROLE_ID in USERS (or a USERNAME in ROLES) but there is neither.

Here is the stack trace. Thanks for any help. I appreciate it, been trying to figure this out for ages!

Hibernate: 
    select
        hibernate_sequence.nextval 
    from
        dual
Hibernate: 
    insert 
    into
        USERS
        (PASSWORD, FIRST_NAME, LAST_NAME, USERNAME) 
    values
        (?, ?, ?, ?)
Hibernate: 
    insert 
    into
        ROLES
        (ROLE_ID) 
    values
        (?)
Hibernate: 
    insert 
    into
        ADMINS
        (ROLE_ID) 
    values
        (?)
Hibernate: 
    update
        ROLES 
    set
        ROLE_ID=? 
    where
        ROLE_ID=?
Exception in thread "main" javax.persistence.RollbackException: Error while commiting the transaction
    at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:71)
    at com.fdm.tradingplatform.dao.JPAManager.persist(JPAManager.java:21)
    at com.fdm.tradingplatform.dao.UserDAO.persist(UserDAO.java:21)
    at Main.main(Main.java:28)
Caused by: org.hibernate.exception.SQLGrammarException: Could not execute JDBC batch update
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:67)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
    at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:249)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:143)
    at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
    at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
    at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
    at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:54)
    ... 3 more
Caused by: java.sql.BatchUpdateException: ORA-01722: invalid number

    at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:343)
    at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:10768)
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:1723)
    at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
    at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:242)
    ... 11 more

There is a problem with this attribute:

@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "ROLE_ID")
private Set<Role> roles;

You're trying to use the id of a role as the foreign key to a user. Instead, Role should have an User attribute, and you should use this attribute to map the roles in the user class.

Add user in the Role class:

@Entity(name = "ROLES")
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class Role implements Serializable{

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name = "ROLE_ID")
    int id;

    @ManyToOne
    @JoinColumn(name = "USER_ID")
    User user;

    ...
}

And in the User class, change the set of roles to this:

@OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Role> roles;

I think the issue is you're making a new Admin(), but not persisting it. You would need to persist it, so that it has an ID, so that you can add it to the User. Otherwise, you're adding a null ID to an int/long id field, and it's throwing that exception.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM