2 Objects dependent on each other. What now? - java

I did this simple Spring Security tutorial. https://www.boraji.com/spring-mvc-5-spring-security-5-hibernate-5-example
Now I want to create a user in my database.
The problem is that I need to create 2 objects dependent on each other and I get
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing:
So What's the solution for this??
public static boolean createUser(byte[] image, String name, String username, String password, String permissions) {
String hashedPassword = new BCryptPasswordEncoder().encode(password);
//check if user already exists
boolean exists = User.checkIfUserExists(username);
//if it doesn't add to database
if(!exists) {
UserRole userRole = new UserRole();
User user = new User();
userRole.setRole(permissions);
userRole.setUser(user);
Database.addToDatabase(userRole);
//user table
user = new User(image, name, username, true, hashedPassword, userRole);
Database.addToDatabase(user);
//user role table
userRole.setUser(user);
Database.updateObject(userRole);
return true;
}
else
return false;
}
//Save the object to the database
public static void addToDatabase(Object object) {
SessionFactory factory = HibernateUtil.GetSessionFactory();
Session session = factory.openSession();
Transaction tx = null;
try{
tx = session.beginTransaction();
session.save(object);
tx.commit();
}catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
}finally {
session.close();
}
}
Basically UserRole already needs to be in db to save User and User needs to be in db to save UserRole.

That exception means exactly what is written. You have a relationship between two objects. For example UserRole has a linked User. But when you are trying to save the UserRole into the database the User object you have attached to it is not yet saved (it is in transient state) so Hibernate's problem is how to save that user role in the database when the user might not exist (it still doesn't have valid ID in the database ).
There are different ways you can deal with that. One of them is to mark the list as Cascade=ALL (or Cascade=SAVE_UPDATE). This way you will bind these two objects and when you save the user it will automatically save the user role. There are pros and cons with that. The "+" is that it is easier and you will have just one save. The "-" is that you have these objects connected and you might save/update an object by mistake if you are not careful.
Another solution would be to first save the User (without the roles). Then save the roles with the user set to the newly saved user (it should have a real ID in the database). Then add the roles to the user and update it. This way you will not save anything with transient values and you will go around that problem. Also do it in a transaction so you don't have bad data if something breaks.

Related

Hibernate - 1 Class 2 Tables

I have a simple table and I want to store them in another table aswell (create a historic of users). Let's call it Users. I created a table exactly like Users (only different id name) called HISTORY_Users.
So I created the table and now I realize that I have 2 tables for 1 object. So how do I add my object only to the hist table?
I dont want to add them at the same time. I want to add the user to the hist only when he deletes the accounts.
I'm using Hibernate with xml mapping
//when the user deletes the account I call this function and pass the User
private static void addToHist(User User) {
//how do I add only to HIST_Users table??
Database.addToDatabase(user);
}
//Save the object to the database
public static void addToDatabase(Object object) {
SessionFactory factory = HibernateUtil.GetSessionFactory();
Session session = factory.openSession();
Transaction tx = null;
try{
tx = session.beginTransaction();
session.save(object);
tx.commit();
}catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
}finally {
session.close();
}
}
Maybe better solution will be use the Hibernate Envers: http://hibernate.org/orm/envers/
You can configure when you want to put it into history: on create, update or delete.
Tables are created automatically, you need to add some annotations only like #Audited etc.

What is difference between save() and update() in Hibernate

I am developing user module using jsp/servlet and hibernate. While updating user I was using same save method which is used while adding new user.
User service
public static User updateUserInstance(HttpServletRequest request) {
String id = request.getParameter("userId");
String firstName = request.getParameter("firstName");
String lastName = request.getParameter("lastName");
String userName = request.getParameter("userName");
String gender = request.getParameter("gender");
String dob = request.getParameter("dob");
String email = request.getParameter("email");
String userType = request.getParameter("userType");
User user = getUser(Integer.parseInt(id));
System.out.println("User is : "+user);
user.setFirstName(firstName);
user.setLastName(lastName);
user.setUserName(userName);
user.setGender(gender);
user.setDob(dob);
user.setEmail(email);
user.setUserType(userType);
return user;
}
UserDao
public boolean saveUser(User user) {
try{
SessionFactory factory = HibernateUtility.getSessionFactory();
Session session = factory.getCurrentSession();
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
return true;
} catch(Exception e) {
System.out.println(e);
return false;
}
}
public boolean updateUser(User user) {
try{
SessionFactory factory = HibernateUtility.getSessionFactory();
Session session = factory.getCurrentSession();
session.beginTransaction();
session.update(user);
session.getTransaction().commit();
return true;
} catch(Exception e) {
System.out.println(e);
return false;
}
}
So my service is finding user based on Id and updating user values. After that I was using same save method of userDao and instead of updating that user It is creating new user. So I have created new method called updateUser.
I tried to find difference between save and update and found that save will insert into database if identifier doesn’t exist else it will throw an error. While in my case its not showing any error and creating new user. So I am not able to clearly identify how save and update internally works.
What are the differences between the different saving methods in Hibernate?
My question is quite different. I want to know same but its clear mentioned that in above question that "save Persists an entity. Will assign an identifier if one doesn't exist. If one does, it's essentially doing an update. Returns the generated ID of the entity." I asking same, My object has identifier still its creating new object instead of updating old object.
RTFM.
save():
Persist the given transient instance, first assigning a generated identifier. (Or using the current value of the identifier property if the assigned generator is used.)
update():
Update the persistent instance with the identifier of the given detached instance. If there is a persistent instance with the same identifier, an exception is thrown.
Summary:
Save is for new things. Hibernate assigns an id as required
Update is for old things. Hibernate uses the id the object as is

Assign existing child entity for parent entity JPA- GAE

I have 2 entity that have a relationship between them.
both of the entities has already created and inserted to the DB.
in some point the user has the ability to connect between them.
when im try doing it im getting:
javax.persistence.PersistenceException: Detected attempt to establish
WannaMeetUser("654321") as the parent of WannaMeetUser("123456") but
the entity identified by WannaMeetUser("123456") has already been
persisted without a parent. A parent cannot be established or changed
once an object has been persisted.
this is a transitive relation (user can have many friends from king of user):
the code is attached:
#Entity
public class WannaMeetUser {
#Id //signifies the primary key
#Column(name = "ID", nullable = false)
private Key id;
#ManyToMany
#Basic
private List<WannaMeetUser> userFriends = new ArrayList<WannaMeetUser>();
}
public void addFriendToWannaMeetUser(#Named("userId") String userId,
#Named("friendId") String friendId) {
EntityManager mgr = getEntityManager();
try
{
WannaMeetUser user = mgr.find(WannaMeetUser.class, WannaMeetServerUtils.getKeyFromString("WannaMeetUser", userId));
WannaMeetUser friend = mgr.find(WannaMeetUser.class, WannaMeetServerUtils.getKeyFromString("WannaMeetUser", friendId));
String coupleId = getcoupleId(userId.toString(), friendId.toString());
if (friend == null || user == null) {
throw new EntityNotFoundException("Object does not exist");
}
WannaMeetCouple couple=mgr.find(WannaMeetCouple.class, coupleId);
if (couple == null) {
couple = createCouple(userId.toString(), friendId.toString());
couple.setId(coupleId);
setUserJoined(couple, userId.toString(), friendId.toString(), true);
}
else {
if (isFriendAllready(couple, userId.toString(), friendId.toString()))
;
setUserJoined(couple, userId.toString(), friendId.toString(), false);
doAddFriend(user, friend, 10, 12321321);
mgr.persist(couple);
mgr.persist(friend);
mgr.persist(user);
}
} catch (Exception e) {
e.printStackTrace();
}
finally{
mgr.close();
}
}
my question is What is the best way to crate such a relationship ?
Thanks
The messages says user 1 has already been persisted without having any parent, but now we try
to persist user 2 as a parent of user 1, but user 1 is known not to have a parent.
try to persist everything in one go using cascading instead. You can declare a cascading relation like this:
#ManyToMany(cascade=CascadeType.ALL)
private List<User> userFriends;
And then persist everything in one go using cascading:
User user1 = new User();
User user2 = new User();
user1.getUserFriends().add(user2);
// this persists the whole tree in one go
entityManager.persist(user1);
Parent-child entities should be used only for relationships that never change (e.g. a user and his photo). For dynamic relationships you have three options:
(A) Store references to other objects as a property (e.g. userFriends property in a User entity). If a relationship is unidirectional (e.g. User A likes User B), then you can update only one entity. For bidirectional relationships, you update both entities.
(B) Create a new entity type Relationship with two properties User A and User B. Create and delete these entities as necessary.
(C) When User A likes User B, create a new entity Relationship as a child entity of User A and use an id of User B as an id for this new Relationship entity.
The choice between these options depends on your data model and data access patterns.

role field not stored in database after identitymanager role creation

I'm using seam 2.1.2 security and I create a role using identitymanager by jsf richfaces GUI. in creation process I would add a "description" to role created in database , the creation of role is ok but the description field is not added, I must update the role (in other operation by GUI) to see the description field stored in database. the question is what have I to do to store the "description" filed in creation operation?
bellow is my code:
public boolean createRole(Role role) {
IdentityManager identityManager = IdentityManager.instance();
logger.info("creating role:" + role.getRoleName());
if (identityManager.roleExists(role.getRoleName())) {
return false;
}
if (identityManager.createRole(role.getRoleName())) {
completeRolePersistence(role);
return true;
}
return false;
}
and the completeRolePersistence method:
public void completeRolePersistence(Role role) {
logger.info("setting additional info to role...");
Query query = em.createNamedQuery("UPDT_ADDITIONALS");
query.setParameter("name", role.getRoleName())
.setParameter("description", role.getDescription())
.setParameter("level", role.getRoleLevel());
int updated = query.executeUpdate();
logger.info("roles updated with desc : " + updated);
}
it seem that the query is not executed , i print in log
roles updated with desc : 0
The reason your code does not work is because your update query is executing BEFORE the role is actually inserted into the database, thus it updates nothing.
My advice is to not use the role/user creation methods in IdentityManager. You're better off creating Home components to create/edit your roles and users than having IdentityManager do it for you.
If you however want to pursue this, you need to overwrite JpaIdentityStore, customize the createRole method and have it populate the entity as needed.

Working with hibernate/DAO problems

Hello everyone here is my DAO class :
public class UsersDAO extends HibernateDaoSupport {
private static final Log log = LogFactory.getLog(UsersDAO.class);
protected void initDao() {
//do nothing
}
public void save(User transientInstance) {
log.debug("saving Users instance");
try {
getHibernateTemplate().saveOrUpdate(transientInstance);
log.debug("save successful");
} catch (RuntimeException re) {
log.error("save failed", re);
throw re;
}
}
public void update(User transientInstance) {
log.debug("updating User instance");
try {
getHibernateTemplate().update(transientInstance);
log.debug("update successful");
} catch (RuntimeException re) {
log.error("update failed", re);
throw re;
}
}
public void delete(User persistentInstance) {
log.debug("deleting Users instance");
try {
getHibernateTemplate().delete(persistentInstance);
log.debug("delete successful");
} catch (RuntimeException re) {
log.error("delete failed", re);
throw re;
}
}
public User findById( java.lang.Integer id) {
log.debug("getting Users instance with id: " + id);
try {
User instance = (User) getHibernateTemplate()
.get("project.hibernate.Users", id);
return instance;
} catch (RuntimeException re) {
log.error("get failed", re);
throw re;
}
}
}
Now I wrote a test class(not a junit test) to test is everything working, my user has these fields in the database : userID which is 5characters long string and unique/primary key, and fields such as address, dob etc(total 15 columns in database table). Now in my test class I intanciated User added the values like :
User user = new User;
user.setAddress("some address");
and so I did for all 15 fields, than at the end of assigning data to User object I called in DAO to save that to database UsersDao.save(user); and save works just perfectly. My question is how do I update/delete users using the same logic?
Fox example I tried this(to delete user from table users):
User user = new User;
user.setUserID("1s54f"); // which is unique key for users no two keys are the same
UsersDao.delete(user);
I wanted to delete user with this key but its obviously different can someone explain please how to do these. thank you
UPDATE :
Do I need to set all 15 fields on User object to delete it like I did with save method ?
Having not looked at Hibernate for quite a while now, I can only hazard a guess at the problem.
It seems that you are creating a User object, but only populating the User ID field, so the persistence layer knows nothing about the actual User.
I would recommend using a retrieve function that can find the User with the given ID, and then pass that User into the delete method.
User u = UsersDao.findById("1s54f");
UsersDao.delete(u);
This should work, as the persistence layer will know about the User, so it has all of the details it needs to perform the delete.
However, a more efficient method would be to find a way of deleting a user by ID, so you do not have to query the database to get the instance of the User and then delete it.
Hope this helps.
Chris
In an ideal world you will have your model's business key as the database primary key and you'll not have this problem. But it ain't so, isn't it?
For you particular problem if you are very much sure that the userID is going to be unique then you can try this (taken from here):
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlDelete = "delete User u where u.userID = :id";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "id", userID )
.executeUpdate();
tx.commit();
session.close();
But let me warn you. This kind of code is not good at all. For example what happens if you decide in future that the column you used in delete is no longer unique? Then you'll run into a very serious bug or a very bad case of refactoring. Either way the fool-proof (may not be efficient & may not be feasible) way is to delete records based on their primary key.
Check out the documentation. Get used to the concept of persistent, transient, and detached instances. To delete an instance, you call
session.delete(persistentInstance)
and to update (although you probably shouldn't need to use it), call
persistentInstance = session.merge(detachedInstance)
Shouldn't need to use update? No, because you just need to load/find an object first, and then modify it. Any modifications you make to a persistent object will automatically be saved back to the database.
In order to delete the user that its ID is "1s54f" you should create a delete HQL as follows:
public void delete(String id) {
log.debug("deleting Users instance");
try {
final String deleteQuery = "delete from User where id = :id";
final Query query = getSession().createQuery(deleteQuery);
final query.setString("id",id);
final int rowCount = query.executeUpdate(); // check that the rowCount is 1
log.debug("delete successful");
} catch (RuntimeException re) {
log.error("delete failed", re);
throw re;
}
}
Then you can use this method like:
userDao.delete("1s54f");
Hope this helps.
Hibernate is an object-relational mapper, that is, it translates between the world of relational databases and object-oriented Java code. Since primary keys are a database concept, it is hibernate's job to translate them into object-oriented terms (in this case: object identity).
That is, if you pass primary keys to hibernate, you are not using hibernate as intended; calling code should represent persistent data with mapped objects, not primary keys. This also allows hibernate to automatically guard against lost updates by checking version numbers.
The typical pattern therefore is to have the following signature:
interface UserDAO {
void delete(User user);
}
and require the DAOs caller to come up with a persistent object to pass to it. The caller might have such an object lying about from the current or a previous (now closed) session, after all, he did somehow learn about its primary key. If all else fails, you can use session.load(User.class, id) to ask hibernate for a proxy to pass to the delete method. (Note that one shouldn't use session.load if the object might no longer exist in the database.)
It's not necessary fetch a whole entity before removing it, neither create a hardcoded query for delete, nor setting every field in entity.
Maybe better way to do that is setting the id for entity and use Hibernate API itself.
If a specific dao is used to entity User, as described in question, try:
public void remove(Serializable id) throws InstantiationException, IllegalAccessException {
User user = new User();
getSessionFactory().getClassMetadata(getEntityClass()).setIdentifier(user, id, (SessionImplementor) getSessionFactory().getCurrentSession());
getHibernateTemplate().delete(entity);
}
As can be seen, neither unnecessary operation in database is made.
And this can be used in generic flavor if GenericDao is implemented like:
public void remove(Serializable id) throws InstantiationException, IllegalAccessException {
Model entity = entityClass.newInstance();
getSessionFactory().getClassMetadata(getEntityClass()).setIdentifier(entity, id, (SessionImplementor) getSessionFactory().getCurrentSession());
getHibernateTemplate().delete(entity);
}
Both ways, Dao must extend org.springframework.orm.hibernate4.support.HibernateDaoSupport to get advantages.
Here's a fragment of generic:
public class GenericDaoImpl<Model> extends HibernateDaoSupport implements GenericDao<Model> {
private Class<Model> entityClass;
public GenericDaoImpl() {
this.entityClass = (Class<Model>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
/* CRUD are implemented here */
public void remove(Serializable id) throws InstantiationException, IllegalAccessException {
Model entity = entityClass.newInstance();
getSessionFactory().getClassMetadata(getEntityClass()).setIdentifier(entity, id, (SessionImplementor) getSessionFactory().getCurrentSession());
getHibernateTemplate().delete(entity);
}
}

Categories

Resources