My problem is that Hibernate does not read any data from DB if it was already inserted manually (I'm using MySQL). What I mean is that I dropped PATIENTS table and then Hibernate created it for me and after that I inserted data from PATIENTS.sql. When I launch my application there are no patients displayed in, for example, show_patients.jsp. But I still can access my application via login page using inserted records.
Here is my method in #Controller:
#RequestMapping(value = "/therapist/showPatients", method = RequestMethod.GET)
public String showExistingPatients(ModelMap map) {
List<Patient> patientsList = userService.getAllPatients();
map.addAttribute("patientsList", patientsList);
return "therapist/show_patients";
}
Here is my method in UserDAOImpl:
public List<Patient> selectAllPatients(){
DetachedCriteria criteria = DetachedCriteria.forClass(Patient.class);
List<Patient> patients = null;
try{
patients = getHibernateTemplate().findByCriteria(criteria);
} catch (DataAccessException ex){
ex.printStackTrace();
}
return patients;
}
The only way I can see any patients in show_patients.jsp is to add new patient via my application allowing Hiberante to save it. So my question is if there is any condition why this works only after Hibernate's save() or am I doing something wrong?
Two variants:
Query cache is turned on in Hibernate. Check settings; maybe, "evict" can be used for remove query results from cache.
Maybe, web page caching present on WebServer or browser settings, check required.
Related
I have encountered a curious bug or feature while writing code. Here's the situation:
We are using a PostgreSQL database, EclipseLink in a JavaEE project.
What I am doing in this part of the project is fetching an entity from the database i.e.:
User user = userController.get(userId);
Which then goes to our controller and fetches the user via a TypedQuery:
#Stateless
#LocalBean
public class UserController {
private EntityManager em;
public User get(Integer userId){
User retval = null;
TypedQuery<User> = em.createNamedQuery("User.findByUserId", User.class);
q.setParameter("userId", userId);
retval = q.getSingleResult();
}
public User update(final User modified){...}
}
And in my User class I have:
#NamedQuery(name = "User.findByUserId", query = "SELECT u FROM User u WHERE u.id = :userId"),
So the call goes, I get my user object with its respective data from the database.
In the class where I called the userController.get method I continue to modify the data on this object, and call our controller again to update this data on the database
user.setServiceId(1); //any id (integer) pointing to an existing service, this is a ManyToOne relationship
userController.update(user);
And here is where it gets funny. In our update method inside the controller class I have my modified User object and using this object I get the primary key userId and fetch the data again from the database to get the original:
#Stateless
#LocalBean
public class userController {
private EntityManager em;
public User get(Integer userId){...}
public User update(final User modified){
User retval = null;
if(modified != null){
try {
User original = get(modified.getId()); //Here I fetch the current state of the DB
if(original != null){
Set<Modifications> modifications = apply(original, modified); //Method to apply modifications
retval = em.merge(original); //Merge changes into database
em.flush(); //Force data to be persisted
catch(Exception e){
}
return retval;
}
}
However, the fields in the original object do not reflect the state of the database but instead contains the same data as the modified object. In this case, the serviceId on the database is null, and in the modified I set it to an ID. The original has its serviceId set to the same value as the modified object even though it should contain the fetched data from the database, in this case null
My current solution is to construct a new User object, after fetching the user from the database, and modify the data on that new object:
User user = userController.get(userId);
User newUser = new User(user);
newUser.setService(service);
userController.update(newUser);
Now when I do the update method, the original reflects the state of the database.
Or maybe it reflects the state of the user object that already exists in the persistence context?
But why does this happen? Since I do make a new get call with a SELECT statement to the database in my update method.
You are using the same EntityManager for everything, both the read and the 'merge', which in this case is then a no-op. Everything read in through an EM is managed, so that if you read it back again, you get the same instance back. As long as the User isn't being serialized, it is 'managed' by the EntityManager it was read from, and so that same instance, and its changes, are visible on any get calls on that ID.
You didn't show how you are getting EntityManagers, but I would guess is isn't container managed, as they would inject a new one for these calls, and then close them for you when done. You haven't shown any transaction logic on how the update and the em context it is using are hooked up, but I would suggest you create a new EntityManager for these calls. Flush also seems unnecessary, as if update is wrapped in a transaction, should handle flushing the update statement to the database without this extra call.
If user.setServiceId(1); is called when the "user" entity is managed, the call is going to update the database row.
you can check the manage entity lifecycle
You need to refresh the data after saving it to the database and to get the latest state of the object, as em.refresh(retval)
You can find the code added below.
#Stateless
#LocalBean
public class userController {
private EntityManager em;
public User get(Integer userId){...}
public User update(final User modified){
User retval = null;
if(modified != null){
try {
User original = get(modified.getId()); //Here I fetch the current state of the DB
if(original != null){
Set<Modifications> modifications = apply(original, modified); //Method to apply modifications
retval = em.merge(original); //Merge changes into database
em.flush(); //Force data to be persisted
em.refresh(retval); // This will fetch the updated data from database
catch(Exception e){
}
return retval;
}
}
I'm using JPA with Hibernate 5.2.10.Final (Oracle database), and deploying on Weblogic 12.2.1.
Let's say I have 2 tables: Customer and LastActivity:
Customer {
id int,
name String,
last_activity_id int not null
}
LastActivity {
id int,
customerName String,
date Date
}
There is a One to Many relationship: a Customer has a single Activity and one Activity has many Customers.
I have a functionality of adding a Customer, when it happens the record in LastActivity table must be created if it doesn't exist for that Customer, otherwise the date must be updated.
My code looks like this (simplified for the purpose of the question):
public Response createCustomer(Request request) {
String name = request.getName();
Customer customer = new Customer(name);
LastActivity activity = activityDao.findByCustomerName(name)
.orElseGet(LastActivity.from(name));
activity.setDate(ZonedDateTime.now());
customer.setActivity(activityDao.update(activity));
return Response.of(customer);
}
My update method is straightforward:
return entityManager.merge(entity);
When I add a new Customer and an Activity that doesn't exist yet ― it is created correctly with the date I specified. The problem is when the activity already exists ― the update doesn't happen. In the logs there is just a select query on Activities table, then correct insert on Customers table, but the date is old.
Some things I tried:
public T update(T entity) {
EntityManager manager = getEntityManager();
T updated = manager.contains(entity) ? entity : manager.persist(entity);
manager.flush();
return updated;
}
Same thing, nothing changed. Also:
Without flushing
Doing merge instead of just returning entity when contains returns true
Just a flush by itself
Nothing since the entity is "attached"
Tried adding CascadeType.MERGE...still nothing. Only thing that worked was this:
public T update(T entity) {
EntityManager manager = getEntityManager();
manager.detach(entity);
return manager.merge(entity);
}
It did what I wanted it to do, but it added extra select query on Activity table (simply by ID, but still, I would like to avoid that).
I actually managed to "solve" the problem by using CriteriaUpdate, but I don't like this and it seems like I lack of some fundamental knowledge about JPA/Hibernate so I don't just want to leave it like this.
I have got a Springboot Application and a Oracle DB with lots of PL/SQL Procedures and these change the state of the DB all the Time.
So now I want to change a loaded entity an want to save it. If the entitystate of the entitymanager and the state of the db is equal everything works fine. But in some cases they are not equal. So if I load an entity and make some changes an druring this a PL/SQL Procedure changes the DB Table. If I save the Entity I will get an Execption of course. So I tried to catch the Exception and then in the catch block I want to refresh the Entity before saving it. But I still get an Exception. Is the Transaction not jet finished? How can I handle this Problem?
I hope the example code explains a little bit.
#RestController
#RequestMapping("/*")
public class FacadeController {
...
#ResponseStatus(HttpStatus.OK)
#RequestMapping( value= "/test4" , method=RequestMethod.GET)
public String test4(){
Unit unit = unitSvice.loadUnit(346497519L);
List<UnitEntry> entries = unit.getEntries();
for (UnitEntry g : entries) {
if (g.getUnitEntryId == 993610345L) {
g.setTag("AA");
g.setVersion(g.getVersion() + 1);
g.setstatus("SaveOrUpdate");
}
}
//<-- DB Table changed entity managed by entitymanger and DB Table
// are no langer equal.
try {
unitSvice.updateUnit(unit , false);
}catch(DataAccessException | IllegalArgumentException e) {
unitSvice.updateUnit(unit , true);
}
...
}
}
#Service("unitSvice")
public class UnitSvice {
#Autowired
private UnitDao repoUnit;
#Transactional
public Unit loadUnit(Long _id) {
Unit unit = repoUnit.findOne(_id);
return unit;
}
#Transactional
public void updateUnit(Unit unit, boolean _withrefrsh ) {
if(_withrefrsh) {
getEntityManager().refresh(unit.getId());
}
repoUnit.save(unit);
}
}
I hope, anyone can help me.
Thanks
yes the problem is ..when you call load all method which is transactional method where entities became detached from session/entitymanager when you are returning from that method.. so,next you are trying to persist detached object. That's why you get exception.
so probably you can use session.update() or session.merge() to save the new update into database.
I am using Liferay to develop a module. A part of involves fetching only those records form the database where the leave status of employees is Pending. the code that I have to fetch "Pending" records is:
#SuppressWarnings("unchecked")
public static List<Employee> getEmployeeData() throws PortalException, SystemException{
List<Employee> employeeDetails;
try{
int totalEmployees = EmployeeLocalServiceUtil.getEmployeesCount();
for(Employee emp: employeeDetails {
if(emp.getEmpStatus.equals("Pending") {
employeeDetails= EmployeeLocalServiceUtil.getEmployees(0,totalEmployees);
}
}
}catch(SystemException se){
employeeDetails = Collections.emptyList();
}
return employeeDetails;
}
The above code fetches all the details - Pending as well as non pending. This I know happens because of the statement in the above code:
employeeDetails= EmployeeLocalServiceUtil.getEmployees(0,totalEmployees);
since It fetches all the rows. So how should I structure and modify my code to get only the pending details?
As you are dealing with custom entity in Liferay, you can use finder tag in service.xml for such scenario. In service.xml define finder for empStatus field.
<finder name="EmpStatus" return-type="Collection">
<finder-column name="empStatus"/>
</finder>
This will create finder[findByEmpStatus(String status)] method in **Persistence.java which will return specific rows based on status , now You need to manually add methods to your *(Local)ServiceImpl.java files. Those methods will call your *persitence.finderMethodName() methods.
HTH,
A quick but really bad practice is keeping your code with this change :
List<Employee> employeeDetails = new ArrayList<Employee>;
try{
List<Employees> allEmployees = EmployeeLocalServiceUtil.getAllEmployees();
for(Employee emp: allEmployees {
if(emp.getEmpStatus.equals("Pending") {
employeeDetails.add(emp);
}
}return employeeDetails;
Now, the correct way to do this is :
add a Finder, as #Pankaj Kathiriya already proposed. Then, build services
go to EmployeeLocalServiceImpl, and add
public List getAllEmployeesByEmpStatus (String status) {
try {
return employeePersistence.findByEmpStatus(status);
} catch (SystemException e) {
e.printStackTrace(); return Collections.emptyList();
}
}
then build service again
Replace your code with
List employeeDetails = EmployeeLocalServiceUtil.getAllEmployeesByEmpStatus("Pending") ;
return employeeDetails;
I have a solution for this. I got a suggestion in the forum to use Dyanamic Query in liferay.
And it is pretty simple.
Read the wiki on Dynamic Query on liferay.
Well to answer my own question, here is the solution I found:
DynamicQuery dynamicQuery = DynamicQueryFactoryUtil.forClass(LeaveApplication.class);
dynamicQuery.add(PropertyFactoryUtil.forName("EmpStatus").eq("Pending"));
and Then I return of those people whose status is pending..
Hope it helps someone.. More suggestions are welcome :)
i've hit a block once again with hibernate.I've posted numerous times on different aspects of the user and contact management that i've been building.
The sad thing is that i didn't really have the time to play with it and understand it better before actually starting working with it. Sorry but English is not my native language, i rather speak french. And again i've started coding in java in an autodidact way.i'm doing all of this by reading books and haven't gone to school for it. with time constraints it's hard to read a book from beginning to the end.
I'm not sure i should put every of my codes dealing with an issue here and from what i've learned from other forum is to post just the necessary and being concise.
So in my User model i have UserAccount class, Profile that holds details like name, preferences etc , AccountSession and Phone.
my contact management model have Contact and Group.
UserAccount has one-to-one association with Profile, one-to-many with AccountSession,contact and group, all bidirectional.the one-to-many association with phone is unidirectional because contact also has and unidirectional with Phone.
Contact has a bidirectional many-o-many with group and one-to-many with phone that i said earlier.
Group also has a many-to-many bedirectional with contact.
here are the mappings
// UserAccount
......
#OneToOne(targetEntity=UserProfileImpl.class,cascade={CascadeType.ALL})
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
#JoinColumn(name="USER_PROFILE_ID")
private UserProfile profile;
#OneToMany(targetEntity=ContactImpl.class, cascade={CascadeType.ALL}, mappedBy="userAccount")
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Contact> contacts = new HashSet<Contact>();
#OneToMany(targetEntity=GroupImpl.class, cascade={CascadeType.ALL}, mappedBy="userAccount")
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Group> groups = new HashSet<Group>();
.......
//Group
#ManyToOne(targetEntity=UserAccountImpl.class)
#JoinColumn(name="USER_ACCOUNT_ID",nullable=false)
private UserAccount userAccount;
#ManyToMany(targetEntity=ContactImpl.class,cascade={CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name="GROUP_CONTACT_MAP", joinColumns={#JoinColumn(name="GROUP_ID")},
inverseJoinColumns={#JoinColumn(name="CONTACT_ID")})
private Set<Contact> contacts = new HashSet<Contact>();
//Contact
....
#ManyToOne(targetEntity=UserAccountImpl.class)
#JoinColumn(name="USER_ACCOUNT_ID",nullable=false)
private UserAccount userAccount;
#ManyToMany(targetEntity=GroupImpl.class, mappedBy="contacts")
private Set<Group> groups=new HashSet<Group>();
....
// helper methods from group
public void addContact(Contact contact) {
try{
this.getContacts().add(contact);
contact.getGroups().add(this);
}catch(Exception e) {
}
}
//helper method from group
public void removeContact(Contact contact) {
contact.getGroups().remove(contact);
this.getContacts().remove(contact);
}
//helper method from contact
public void addGroup(Group group) {
try{
this.getGroups().add(group);
group.getContacts().add(this);
} catch(Exception e) {
e.printStackTrace();
}
}
//Helper method from group
public void removeGroup(Group group){
try{
group.getContacts().remove(this);
this.getGroups().remove(group);
} catch(Exception e) {
e.printStackTrace();
}
}
//UserAccount setter from Contact.All the children with many-to-one have the same
/**
* #param userAccount the userAccount to set
*/
public void setUserAccount(UserAccount userAccount) {
this.userAccount = userAccount;
}
I'ld like to pull the UserAccount by its email field which is an unique field in the UserAccount table.
In the UserAccountDAO the method i call to get the UserAccount is getUserAccountByEmail here below.So i expect this method to load all the children collections of the UserAccount namely its Contact collection, group collection.I want it in such a way that when UserAccount is loaded with Contacts collection each of the contact object has its reference with its belonging groups collection if any etc and vice versa.
public UserAccount getUserAccountByEmail(String email) {
// try {
logger.info("inside getUserAccountByEmail");
logger.debug(email);
Session session = (Session) this.getDBSession().getSession();
UserAccount user = (UserAccount) session.createCriteria(this.getPersistentClass())
.setFetchMode("contacts", FetchMode.SELECT) //recently added
.setFetchMode("groups", FetchMode.SELECT) // recently added
.add(Restrictions.eq("email", email))
.uniqueResult();
logger.debug(user);
return user;
// } catch(NonUniqueResultException ne) {
// logger.debug("Exception Occured: getUserAccountByEmail returns more than one result ", ne);
// return null;
// } catch(HibernateException he){
// logger.debug("Exception Occured: Persistence or JDBC exception in method getUserAccountByEmail ",he);
// return null;
// }catch(Exception e) {
// logger.debug("Exception Occured: Exception in method getUserAccountByEmail", e);
// return null;
// }
Since there has to be an UserAccount before any contact and groups, in my unit test when testing the saving of a contact object for which there must be an existing group i do this in order
a create userAccount object ua.
b create group object g1;
c create contact object c1;
d ua.addGroup(g1);
e c1.setUserAccount(ua);
f c1.addGroup(g1);
g uaDao.save(ua); // which saves the group because of the cascade
h cDao.save(c1);
Most of the time i use the session.get() from hibernate to pull c1 by its it id generated by hibernate and do all the assertions which works actually.
but in Integration test when i call getUserAccountByEmail with and without the setFetchMode and it returns the right object but then all the children collections are empty. i've tried the JOIN and the SELECT.the query string changes but then the result set is still the same. So this arises some questions :
1. What should i do to fix this?
2. the helper method works fine but it's on the parent side(i do it in the test).What i've been wondering about is that doing c1.setUserAccount(ua); is enough to create a strong relationship between UserAccount and contact.most of the time there will not be cases where i save the userAccount with contact but yet the helper method that set the association in both side and which is in UserAccount will not been called before i save the contact for a particular userAccount.So i'm little confused about that and suspecting that setting of the association is part of the why something is not working properly.and then calling session.get(UserAccount.class, ua.getID()) i think goes what i want and i'ld like getUserAccountByEmail to do the same.
3. ChssPly76 thinks the mapping has to be rewrite.So i'm willing to let you guide me through this.I really need to know the proper way to do this because we can't lean everything from a good book.So i you think i should change the mapping just show me how.and probable i'm doing things the wrong way without even been aware of that so don't forget i'm still learning java itself.THanks for the advise and remarks and thanks for reading this
I agree with you that it seems likely that the associations between your parent objects and their child collections are not getting persisted properly. I always like to start out by looking at what is in the database to figure out what's going on. After you run your test what do you see in the actual database?
It seems likely that one of two things is happening (using UserAccount as an example):
The items in the child collection are not getting saved to the database at all, in which case you'll be able to see in the database that there are no records associated with your UserAccount. This could be caused by saving the UserAccount object before you've added the child object to the UserAccount's collection.
The items in the child collection are getting saved to the database, but without the needed association to the parent object, in which case you'll see rows for your child items but the join column (ie 'userAccount' will be null). This could be caused by not setting the userAccount() property on the child object.
These are the two scenarios that I've run into where I've seen the problem you describe. Start by taking a look at what goes into your database and see if that leads you farther.