In my controller, put a value from a MySQL
database into a ModelAndView object
There is a separate program that updates the table and the MVC is supposed to grab that value so there are no forms to update that table.
When the table is updated, and when I hit refresh on the browser, the values will not update on the page.
Controller
@SuppressWarnings("unchecked")
@Secured({ "ROLE_USER", "ROLE_ADMIN" })
@RequestMapping(value = { "/", "/welcome" }, method = RequestMethod.GET)
public ModelAndView defaultPage(@ModelAttribute("user") User user) {
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) SecurityContextHolder
.getContext().getAuthentication().getAuthorities();
ModelAndView view = new ModelAndView("/hello");
// Redirects to admin page if user has admin role
if (authorities.toString().contains("ROLE_ADMIN")) {
return new ModelAndView("redirect:/admin");
}
/////
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String userName = auth.getName();
User userInfo = userDAO.getUserInfo(userName);
System.out.println("Here's the thing " + userInfo.getLastname() + " " + userInfo.getUserDetails());
Details details = userDAO.getDetailsInfo(userInfo.getUserDetails().getLastname(),
userInfo.getUserDetails().getPostcode());
Plugs plugs = userDAO.getPlugInfo(details.getMacAddress());
String json = plugs.getJson();
JSONObject obj = new JSONObject(json); //this is the value that is not updating
String name = obj.getJSONObject("meta").getJSONObject("locate").getString("value");
System.out.println(name);
view.addObject("json", obj);
return view;
}
I know this is pretty looked down upon, but I putting that value in Javascript.
like this:
<c:set var="json" value="${json}"/>
var __data__ = ${json};
Why won't the MVC display the correct value when the database is updated?
I want it to update on refresh
EDIT: I've disabled caching and cleared cache still have a problem. Any help?
Definitely PersistenceContextType.EXTENDED
scope is the root cause for your problem as you identified. The reason below:
With PersistenceContextType.TRANSACTION
scope, Spring framework takes responsibility of managing the life cycle of the injected the entitymanager
. The life of TRANSACTION
scope is tied to the underlying transaction
and upon commit/rollback of the transaction the entityManager
is closed by the spring framework.
But with PersistenceContextType.EXTENDED
scope, Spring framework takes responsibility of only injecting the entitymanager
. The life cycle of EXTENDED
scoped entity manager is not tied to the user transaction
and it can span multiple transactions. It is up to the application/user to explicitly close the entityManager
once we are done with it. If not it will remain open forever (or till Spring container is closed).
I presume in your userDAO
you are not closing entityManager
explicitly. And also 'userDAO' could be a singleton. So the same entityManager
that got injected only once is being used across multiple calls (or http requests)
With this the entityManager' remains open forever and so when you try to access any object (User/Plugin/UserDetails) the
entityManager` checks its first level cache, it will check its first level cache which it finds in it (that got loaded for the first time) and returns this object form its first level cache (which is stale) instead of hitting the database for the data.
And obviously with TRANSACTION
scope the entityManager
is closed by the Spring framework whenever transaction is completed (or method exits). This results in creating an entityManager
for each request (in your case web request) hits the the database which has the updated data.
See if this link helps. http://forum.spring.io/forum/other-spring-related/ejb/18445-injection-of-persistencecontext-with-persistencecontexttype-extended
So the solution I have has nothing to do with the code I posted, but in my UserDAO.java class.
Simple change.
From this:
@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager em;
To this:
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
private EntityManager em;
and it works. Thank for the people who commented and tried to help/
EDIT: I hate answering my own questions, so if someone could have an in depth explanation on why this works, I'll accept it as an answer.
http://docs.oracle.com/javaee/6/tutorial/doc/gkjio.html
Disable the JPA second level cache otherwise the entity manager does not recognise changed data which was changed directly in the database
Put
@Cacheable(false)
onto your entity