Reputation: 899
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
Upvotes: 1
Views: 3693
Reputation: 7393
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;
Upvotes: 1
Reputation: 1674
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.
Upvotes: 0