I have a Database where user and address data is stored in separate tables. When a user logs in to my page I want to show him a form with which he can change his user/account data. However, I end up getting an Exception: org.hibernate.LazyInitializationException: could not initialize proxy - no Session. The address attributes can't be loaded. The user loads just fine and that's what I don't understand. My AddressDAOImpl looks like this:
#Autowired
private SessionFactory sessionFactory;
public void addAddress(Address address)
{
sessionFactory.getCurrentSession().save(address);
}
public void updateAddress(Address address)
{
sessionFactory.getCurrentSession().update(address);
}
public List<Address> listAddress()
{
return sessionFactory.getCurrentSession().createQuery("from address").list();
}
public void deleteAddress(int id)
{
Address addr = (Address) sessionFactory.getCurrentSession().load(Address.class, id);
if(addr != null)
{
sessionFactory.getCurrentSession().delete(addr);
}
}
public Address getAddress(int id)
{
return (Address) sessionFactory.getCurrentSession().load(Address.class, id);
}
My Controller does this:
#RequestMapping("/library/home")
public ModelAndView showHome()
{
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userService.getUser(username);
Address address = addressService.getAddress(user.getId());
ModelMap mmap = new ModelMap();
mmap.addAttribute("user", user);
mmap.addAttribute("addresse", address);
return new ModelAndView("/library/home", mmap);
}
Why doesn't this work? And why does it work for the user data?
I'm assuming that your AddressService created the transaction used to load the Address proxy (it's a proxy because you used load() instead of get()). The session associated with this transaction is closed when you return the Address from the AddressService.
By the time your view resolver tries to access the attributes of the Address proxy, there is no session available through which a database query can be run to fetch the attributes. Hence the exception.
Either use get() instead of load(), or access the attributes while you're still in the scope of the transaction.
The load method doesn't fetch an address from the database. It returns a proxy to an uninitialized address, assuming the address exists in the database. Use Session.get to get the address.
See http://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Session.html#load%28java.lang.Class,%20java.io.Serializable%29
I think its possibly because you did not initialize your sessionfactory configuration. Try this class to help you create a session factory.
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
sessionFactory = new Configuration().configure()
.buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
hope this helps
You can consider using an OpenSessionInView pattern even if it's not always the best solution
Related
I need to set a mysql session variable for my application to work with a MariaDB Galera Cluster as expected. The SQL call is: SET SESSION wsrep_sync_wait = 1. It shall be set at all times when the application uses the database. I am using EclipseLink as the JPA provider.
My question is: What is the best way to achieve this?
Option 1: EclipseLink Session Customizer
Register a session customizer in persistence.xml:
public class SessionCustomizerImpl implements org.eclipse.persistence.config.SessionCustomizer {
private final static String WSREP_SYNC_WAIT_CHECK_SQL = "SHOW SESSION VARIABLES LIKE 'wsrep_sync_wait'";
private final static String WSREP_SYNC_WAIT_SET_SQL = "SET SESSION wsrep_sync_wait = 1";
#Override
public void customize(Session session) throws Exception {
Vector result = session.executeSQL(WSREP_SYNC_WAIT_CHECK_SQL);
if ((result != null) && !result.isEmpty()) {
session.executeNonSelectingSQL(WSREP_SYNC_WAIT_SET_SQL);
// Galera connection detected; wsrep_sync_wait set to 1
} else {
// No Galera connection detected; wsrep_sync_wait not set
}
}
}
This does not work for me. Querying the session variable from an EntityManager returns a value of 0.
Option 2: EntityManager factory
Every time a new EntityManager is created, the SQL is executed.
public class SyncWaitEntityManagerFactory implements Factory<EntityManager> {
private final EntityManagerFactory emf;
#Inject
public SyncWaitEntityManagerFactory(EntityManagerFactory emf) {
this.emf = emf;
}
#Override
public EntityManager provide() {
final EntityManager em = emf.createEntityManager();
// set it
em.getTransaction().begin();
em.createNativeQuery("SET SESSION wsrep_sync_wait = 1").executeUpdate();
em.getTransaction().commit();
return em;
}
#Override
public void dispose(EntityManager instance) {
if (instance.isOpen()) {
instance.close();
}
}
}
This works, but I'm not sure if it is overkill. Also, I am worried about the cost of the transaction, which is only required by Query#executeUpdate(), but not by the actual SQL call.
Option 3: Via JDBC URL
Appending the variable and value to the JDBC URL (see here for details):
String jdbcUrl = "jdbc:mysql://db.example.test:3306/"+ JDBC_DB
+"?sessionVariables=wsrep_sync_wait=1";
Properties p = new Properties();
p.put("javax.persistence.jdbc.url", jdbcUrl);
p.put("javax.persistence.jdbc.user", JDBC_USER);
p.put("javax.persistence.jdbc.password", JDBC_PASSWORD);
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPU", p);
EntityManager entityManager = emf.createEntityManager();
Nice solution. Works for me; no effort, no transaction necessary. Downside: I can't catch exceptions (example: check first if the variable exists, then set it -- allows deployment of the code on systems that don't support/use this specific variable).
You could also use an aspect to execute a query every time a getConnection() is called which is for every transaction basically (the aspect is set after the call so that we have a valid connection object):
#Component
#Aspect
public class CustomConnectionPreparer implements ConnectionPreparer
{
#AfterReturning(pointcut = "execution(* *.getConnection(..))", returning = "connection")
public Connection prepare(Connection connection) throws SQLException {
// execute the query (also exception handling)
try (Statement statement = connection.createStatement()) {
statement.execute("SET SESSION wsrep_sync_wait = 1");
} catch (SQLException e) {
throw e;
}
return connection;
}
}
And before you return the connection to the caller you execute your query and you should always have that value set.
I have tried to implement this many ways, but this is the way that makes the most sense to me, and I am still unable to return anything from my resource. I added the resource with the GlassFish admin GUI (essentially, i am trying to save username and passwords on the local server).
While I am getting a null pointer exception (NPE), please do not point me here, it doesn't help me at all. What is a NullPointerException, and how do I fix it?
Here are my supporting classes...
Creating the bean
#LocalBean
#Stateless
public class JndiProperties {
#Resource(name="jndiCreds")
private Properties properties;
public String getUser() {
return properties.getProperty("UserName");
}
public String getPass() {
return properties.getProperty("UserPass");
}
}
This is my bean manager:
#ManagedBean
#ViewScoped
public class GetCreds {
#Inject
private JndiProperties property;
public String getUserName(){
return property.getUser();
}
public String getPassword(){
return property.getPass();
}
}
And this is how I call them
GetCreds creds = new GetCreds();
String username = creds.getUserName();
String pass = creds.getPassword();
I named the resource jndiCreds and have the names UserName and UserPass with the values containing respective data.
Here is the view from the GlassFish GUI:
Have any idea WHY it won't return my requested information? I AM receiving an NPE when I try to call the resource when I call either function from getCreds.
Help would be appreciated; I am very stuck.
I decided to step away from trying to use a bean and just accessing it directly (although I am giving up some security here). I am trying to access the data in a contextual manner. BUT! I still can not do it! Here is my NEW supporting class:
public class JndiProperties {
public Properties getProperties(String jndiName) {
Properties properties = null;
try {
InitialContext context = new InitialContext();
properties = (Properties) context.lookup(jndiName);
context.close();
}
catch (NamingException e) {
return null;
}
return properties;
}
And this is how I grab the information:
JndiProperties creds = new JndiProperties();
String username = creds.getProperties("jndiCreds").getProperty("UserName");
String pass = creds.getProperties("jndiCreds").getProperty("UserPass");
String credentials = String.join(System.getProperty("line.separator"),
"user=" + username,
"password=" + pass);
System.out.print(credentials);
I am using the same resource shown above. I am STILL ending up with null pointer... ANY help would be greatly appreciated.Feel free to answer what was wrong with my bean implementation also.
I have figured it out!
What was happening is first, I was a complete idiot, and I was trying to test my program with JUnit (IT DOES NOT RUN ON THE SERVER!)
Since the program wasn't being run on GlassFish, it couldn't access the resource.
Secondly, (and most importantly) I was missing the appserver-rt.jar- very important.
I have a class, Student and the generated Endpoint class for it. ListStudents and insertStudents methods work without any problems, but update and remove don't cause any change in the datastore. The methods don't throw any errors and the call returns, but no changes are made.
My endpoints code is mostly the code generated by google plugin for eclipse:
#ApiMethod(name = "removeStudent", path="remove_student")
public void removeStudent(#Named("email") String email) {
EntityManager mgr = getEntityManager();
try {
Student student = getStudentByEmailName(email);
mgr.remove(student);
} finally {
mgr.close();
}
}
Entitiy manager getter method:
private static EntityManager getEntityManager() {
return EMF.get().createEntityManager();
}
#ApiMethod(name = "updateStudent")
public Student updateStudent(Student student) {
EntityManager mgr = getEntityManager();
try {
if (!containsStudent(student)) {
throw new EntityNotFoundException("Object does not exist");
}
mgr.persist(student);
} finally {
mgr.close();
}
return student;
}
And my EMF class:
public final class EMF {
private static final EntityManagerFactory emfInstance = Persistence
.createEntityManagerFactory("transactions-optional");
private EMF() {
}
public static EntityManagerFactory get() {
return emfInstance;
}
}
The client that uses this endpoint is Android. I have only tried testing on my local server.
Please tell me if I'm doing something wrong. Thank you
Do you have your student entities indexed by email?
This is a typical issue when you move to nosql and expect all queries to work without indexes.
Note that records inserted before defining index would not be in index.
The datastore is eventually consistent and your code should work. What is the return value that you get in the Student object from your updateStudent method.
As much as I don't want to, after you do a mgr.persist(...) , add mgr.flush() and see if that makes a difference.
In Spring data neo44 we have just repository.save(entity), but for example when my UserEntity's property(email) changed, i dont know how to update the same.
I tried also with neo4j template, but save entity with existing node id caused the below rollback.
org.springframework.dao.InvalidDataAccessApiUsageException: New value must be a Set, was: class java.util.ArrayList; nested exception is java.lang.IllegalArgumentException: New value must be a Set, was: class java.util.ArrayList
at org.springframework.data.neo4j.support.Neo4jExceptionTranslator.translateExceptionIfPossible(Neo4jExceptionTranslator.java:43)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
How we can update node or nodeentity?
public void updateUserNode(UserEntity user) {
try{
UserEntity updatedUser = this.getUserByUserId(user.getUserId());//finding node with user id///
updatedUser.setEmail(user.getEmail());
updatedUser.setImageId(user.getImageId());
updatedUser.setFirstname(user.getFirstname());
updatedUser.setLastname(user.getLastname());
//System.out.println("Deleting ");
//userRepository.delete(del);
System.out.println("UPDATING ");
// with existing Id, you can not save it again/, or update
updatedUser = userRepository.save(updatedUser);
}catch(Exception e){
e.printStackTrace();
}
//return
}
You have to embed the .save() within a transaction.
As an example:
final org.neo4j.graphdb.Transaction tx = this.neoTemplate.getGraphDatabaseService().beginTx();
try {
updatedUser = userRepository.save(updatedUser);
tx.success();
} finally {
tx.finish();
}
In your UserEntity domain object, are you storing any relationships? Be sure they are declared as Set<T> and not as Iterable<T>:
From: http://static.springsource.org/spring-data/data-graph/snapshot-site/reference/html/#reference:programming_model:relationships:relatedto
"It is also possible to have fields that reference a set of node
entities (1:N). These fields come in two forms, modifiable or
read-only. Modifiable fields are of the type Set, and read-only
fields are Iterable, where T is a #NodeEntity-annotated class."
I suspect your default constructor is instantiating an ArrayList...
Since you're using SDN you should not ever be in need to manually start/commit any Transactions.
Suppose your User class looks like this
#NodeEntity(label="User)
public class User extends DomainObject{
#Property(name = "email")
private String email;
//getter and setter
}
and your UserRepository is similar to this:
public interface UserRepository extends GraphRepository<User> {
//maybe this is already right at hand by SDN and thus redundant?
#Query("MATCH (u:User {email:{email}}")
public User findByEmail(#Param("email") String email)
}
Then you may use a #Transactional on a UserService class:
#Component
#Transactional
public class UserService {
#Autowired
private UserRepository userRepository;
public void updateEmail(String email) {
User user = userRepository.findByEmail(email);
if (user == null) return; //or throw...
user.setEmail(email);
userRepository.save(user);
}
}
I'm working on a webapp and I have connection errors after Hibernate throws exceptions :
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
It gave me this exception each time I try to access my db after an exception occured.
I now Hibernate's not supposed to throw errors if my application is well coded but if something happens with the connection to the db, I don't want my application to be stuck with this error.
Here's my HibernateUtil class :
public class HibernateUtil {
private static Logger log = Logger.getLogger(HibernateUtil.class);
private static org.hibernate.SessionFactory sessionFactory;
private static String confFile = "hibernate-test.properties";
private static final ThreadLocal<Session> threadSession = new ThreadLocal<Session>();
private HibernateUtil() {
}
public static void buildSessionFactory(){
Configuration configuration = new Configuration();
synchronized(HibernateUtil.class){
if(sessionFactory == null){
try {
Properties properties = new Properties();
properties.load(HibernateUtil.class.getClassLoader().getResourceAsStream(confFile));
configuration.setProperties(properties);
} catch (Exception e) {
log.fatal("cannot load the specified hibernate properties file: " + confFile);
throw new RuntimeException("cannot load the specified hibernate properties file : " + confFile, e);
}
sessionFactory = configuration.configure().buildSessionFactory();
}
HibernatePBEEncryptorRegistry registry = HibernatePBEEncryptorRegistry.getInstance();
if(registry.getPBEStringEncryptor("strongHibernateStringEncryptor") == null) {
StandardPBEStringEncryptor strongEncryptor = new StandardPBEStringEncryptor();
strongEncryptor.setAlgorithm("PBEWithMD5AndDES"); // not really needed as it is the default
strongEncryptor.setPassword("aStrongPassword");
registry.registerPBEStringEncryptor("strongHibernateStringEncryptor", strongEncryptor);
}
}
}
public static SessionFactory getSessionFactory() {
if(sessionFactory == null){
buildSessionFactory();
}
return sessionFactory;
}
public static Session getCurrentSession(){
if(!getSessionFactory().getCurrentSession().isOpen())
getSessionFactory().openSession();
return getSessionFactory().getCurrentSession();
}
}
Here's my BaseAction class where initialization and closing of sessions is set :
public class BaseAction extends ActionSupport {
public Session hib_session;
public void initHibSession() {
hib_session = HibernateUtil.getCurrentSession();
hib_session.beginTransaction();
hib_session.clear();
}
public void closeHibSession() {
hib_session.getTransaction().commit();
}
}
Here's an example of an action:
Transaction transaction = new Transaction(user, Transaction.Type.REGISTRATION, new HashSet(domains));
initHibSession();
hib_session.save(transaction);
closeHibSession();
transaction_id = transaction.getId();
Is there a way to avoid the exception above ?
It gave me this exception each time I try to access my db after an exception occurred.
I'm not sure to understand the exact condition. Anyway, after an exception, you should rollback the transaction, close the session and start over. That being said, I have some remarks about your code.
About your HibernateUtil:
why do you have a ThreadLocal, the Session#getCurrentSession() method handle that for you (you don't seem to use the thread local though).
in HibernateUtil.getCurrentSession(), why do you mess with getCurrentSession() and openSession()? Firstly, there is no need to do what you do, getCurrentSession() will return a new session if no session is associated to the current thread. Secondly, both approaches are different and have different semantics (you need to close the session yourself when using openSession()), you should use one or the other.
About your BaseAction:
I wonder why you clear() the session after Session#beginTransaction(). In case you didn't committed an ongoing transaction, you'll loose all the pending changes. Is this really what you want?
PS: I would consider using the Open Session in View pattern to remove all this burden from your code.
Resources
Sessions and transactions
Open Session in View