Reusing Hibernate SessionFactory - java

I've got a couple of class instance variables: userSF which is a SessionFactory and userCfg which is a Configuration set up to use user.cfg.xml. When I have someone log into my application their database login information is grabbed from a file the following happens:
userCfg.setProperty("hibernate.connection.username", username);
userCfg.setProperty("hibernate.connection.password", password);
userSF = userCfg.buildSessionFactory();
When the user logs out of the application it closes the session factory. then when another user tries to log in that same code is called with username and password changing based on who is logging in. The problem is that the second time it's called I'm getting an exception:
org.hibernate.HibernateException: HHH000469: The ClassLoaderService can not be reused. This instance was stopped already.
Is there a way to reuse the SessionFactory variable for multiple logins?

You should not create a SessionFactory for each logged in user / configuration. It is a very costly component and should target the Singleton pattern. Use a single instance per context within your application.
If you have multiple configurations you can cache multiple instances in application scope. Depending on your application this maybe a static Singleton class using a Map<MyUserPasswordKey, SessionFactory> or in an JAVA-EE container a #ApplicationScoped EJB or JSF-Bean.
In case a context based on a user-password key will lead to too many SessionFactory instances you maybe should change your application to either manage them in permission groups or restrict your functionality within your data access or service layer.

Related

Can we use multiple db instances in hibernate at a time?

My requirement is, I have two db instances in db2 with same db schema. We take backup of db every year. So we have db like dbinstance_2014, dbinstance_2015 and dbinstance_2016.
My application is working on 2016 instance. I need to create report year wise in that case I need to use older instances. I have create two methods to create session factory. One method will create current years dbinstance connection and other will dynamically create connection to older db.
Current year sessionFactory is used throughout the application. In case of report generation I am trying to use another method which creates sessionfactory dynamically.
But I am getting exception:
org.hibernate.HibernateException: No CurrentSessionContext configured
How can I resolve this error?

No session currently bound to execution context

I got below exception when I used session.getCurrentSession().
I have mentioned
hibernate.current_session_context_class: managed
org.hibernate.HibernateException: No session currently bound to execution context
at org.hibernate.context.internal.ManagedSessionContext.currentSession(ManagedSessionContext.java:75)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
at io.dropwizard.hibernate.AbstractDAO.currentSession(AbstractDAO.java:36)
at io.dropwizard.hibernate.AbstractDAO.persist(AbstractDAO.java:149)
I use this with dropwizard. Can anyone help me to solve this?
If you are using Dropwizard Hibernate. You need to add #UnitOfWork annotation to your Resource method. More info within dropwizard manual, hibernate chapter.
Can you try with : session.openSession() - It tell hibernate to always opens a new session and you have to close once you are done with the operations. With session.getCurrentSession(), hibernate returns a session bound to a context that you don't need to close and only need to set the hibernate.current_session_context_class to thread.
You can also configure session with SpringSessionContext, if your application is Spring based.
Edit your hibernate-cfg.xml with below line:
hibernate.current_session_context_class=org.springframework.orm.hibernate3.SpringSessionContext
What above line will do?
Making session context class as "org.springframework.orm.hibernate3.SpringSessionContext
", Hibernate will assume it is executing inside of a Spring transactional context (i.e. through a Spring transactional aspect) and Spring will now manage your transaction for you. However if you call getCurrentSession() outside of such a context, Hibernate will throw an exception complaining that no Session is bound to the thread.

Web application with one schema per User

I am trying to enable my web application to use one (MySQL) schema per user.
I am using Spring with JPA along with Hibernate.
Is the Hibernate Multi-tenancy concept relevant?
The way I am trying to do it is
(a) have a EntityManagerFactory per HTTPSession and
(b) set the schema in it at login.
I have figured out how to do (b), but I still have issues doing (a).
I tried putting #Scope(WebApplicationContext.SCOPE_SESSION), but what about the global EntityManagerFactory?
Any help?
You can implement your own ConnectionProvider and there make extra settings.
But I think that you have large problems with the architecture your application if you want to do one schema per user.
UPD1
If you use spring. You can try to declare a bean with own impl of ConnectionProvider in Session scope.
But there is a big problem. Hibernate make the ConnectionProvider. It means that you have to impl own ServiceRegistry (with working through Spring) and override StandardServiceRegistryBuilder, and impl EntityManagerFactoryBuilder (based on EntityManagerFactoryBuilderImpl but with your StandardServiceRegistryBuilder).
When new session have been created it'll use ConnectionProvider to create the connection (Probably you'll have to override some classes).
This is really not suggested.. This will harm you later, you can't do differnt schema for diffrent user. You can always create you own properties to the connection..
Configuration cfg = new Configuration();
cfg.configure();
System.setProperty("hibernate.connection.password",pass);
System.setProperty("hibernate.connection.username",usr);
System.setProperty("hibernate.connection.driver_class", driver_class);
System.setProperty("hibernate.connection.url", driver_url);
System.setProperty("hibernate.dialect", dialect);
// etc, etc, for all properties
cfg.setProperties(System.getProperties());
sessionFactory = cfg.buildSessionFactory();
It can be something like that.. But this is WRONG!

Where to store properties from properties file in java webapp

Not sure if this question was previously asked, but I cannot seem to find the answer.
Where does one store the properties when loaded in a webapp.
I have a web application which has settings to allow system administrators via a user interface to change settings in the app.
For example the app allows only selected user groups to be able to go a certain page.
I wanted to allow system admins to go in and set the user groups that can go to the above mentioned page, and then change it at a later date.
The issue I'm facing is that once loaded the properties file, where do I store the data rather than continuously loading the properties file each time a user goes into the page.
I'm probably not getting the full concept of how properties are used so any guidance would be greatly appreciated.
Just make sure I can read the user groups in, can change the user groups without reloading the class/app and allow it to be thread safe and quick without two different threads having two different properties because we are using a load balanced environment. With a content share which is where the properties files are stored and accessed (not having any issues with this so not looking for help with where to store the properties file).
Any help greatly appreciated.
EDIT 1
The application runs on a clustered environment which means that other application servers could potentially have different values due to multiple ServletContexts.
Register ServletContextListener to load Init parameters and properties at server start-up.
Load properties at single time and make it visible to other classes statically or you can store it in application context as well to access it from anywhere such as JSP and Servlet.
Note: Make the properties file location configurable in web.xml rather than hard-coding it in Java class. You can retrieve the properties file location as system environment variable as well.
Sample code:
public class AppServletContextListener implements ServletContextListener {
private static Properties properties;
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
String cfgfile = servletContextEvent.getServletContext().getInitParameter("config_file");
properties.load(new FileInputStream(cfgfile));
// store it in application scope as well
servletContextEvent.getServletContext().setAttribute("prop",properties);
}
public static Properties getProperties(){
return properties;
}
}
web.xml:
<listener>
<listener-class>com.x.y.z.AppServletContextListener</listener-class>
</listener>
<context-param>
<param-name>config_file</param-name>
<param-value>config_file_location</param-value>
</context-param>
Please have a look at my another post that is asked in the same context:
Retrieve Init parameters outside servlet
EDIT
If you are changing the properties at run-time then don't use Servlet context according to the ServletContext javadoc:
In the case of a web application marked "distributed" in its deployment descriptor, there will be one context instance for each virtual machine. In this situation, the context cannot be used as a location to share global information (because the information won't be truly global). Use an external resource like a database instead.
The Servlet specification also states in "SRV.4.4.1 Context Attributes in a Distributed Container":
Context attributes are local to the JVM in which they were created. This prevents ServletContext attributes from being a shared memory store in a distributed container. When information needs to be shared between servlets running in a distributed environment, the information should be placed into a session (See Chapter SRV.7, “Sessions”), stored in a database, or set in an Enterprise JavaBeansTM component.
In that case you can try with some third party cache that works in distributed environment as well as mentioned below:
EHCache
Infinispan
OR store all the properties in the database.
The Servlet container offers the concept of Contexts. I find it helpful to consider a Context as a useful box for storing things in, and operates like a Map.
There are a number of different Contexts available to a Java Webapp, and they differ in scope (that is, how long the data held in the context lasts for, and where it can be accessed from). There is the Page Context, the Session Context and the Servlet Context
The Page Context has the narrowest scope, and is only lasts as long as a single page takes to process.
The Session Context has a greater scope, and lasts as long as single user session, i.e. multiple requests from a browser. It is useful if your webapp requires authentication - information about the authenticated user will be stored in the Session Context.
The Servlet Context is effectively global and is always available to the whole application. This is where I would recommend storing configuration properties which effect the functioning of the application.
In a Servlet, you may access the Servlet Context like this:
ServletContext context = request.getSession().getServletContext();
You can store something in the context like this:
context.setAttribute("key", object);
Where key is a String - the name of the attribute.
You may retrieve it again like this:
object = context.getAttribute("key");
Which returns an Object. You may cast it to whatever type it really is. If you want to, you can store a Properties object in it:
Properties props = //... get the properties from file
context.setAttribute("props", props);
And then retrieve them:
Properties props = (Properties) context.getAttribute("props");
Or you can store the individual properties as separate attributes in the context.
All contexts are accessed the same way.
You could go with the classic singleton pattern, where you have a single ApplicationProperties class which holds globally valid values for your application backed by a property file so no part of your application has to care about how to store the properties. Pseudo code:
public class ApplicationProperties {
private static final String PATH = "app.properties";
private static final ApplicationProperties INSTANCE = new ApplicationProperties();
private String userGroup;
private ApplicationProperties() {
// Load properties from PATH and populate fields.
this.userGroup = ...
}
public static ApplicationProperties getInstance() {
return INSTANCE;
}
public String getUserGroup() {
return this.userGroup;
}
public String setUserGroup(String userGroup) {
// Save to property file to persist.
this.userGroup = userGroup;
}
}
You just have to synchronize access to the fields that no two threads overwrite properties and create race conditions.

Client's Transaction Aborted when accessing two databases

I have a propertiesbean which gives me SqlSessions.
This bean is annotated with all this jazz # the class level
#Singleton
#LocalBean
#Startup
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
i also have this
public SqlSessionFactory getSqlSessionFactory(ConnTypes connType) {...}
which returns the SqlSession you want.
I have two databases. One is a mysql instance and the other one isn't (lets call it db2).
When I run the project locally everything is fine. Both databases are accessed with no problem.
When I run the project on our test server it starts to throw a Client Transaction aborted error. I've done a fair amount of research on this and it seems like people get these exceptions when there is a problem with a database query or some database access. The entire transaction is then marked as rollback and this exception is thrown (at least that's what i've read)
It looks to me like it throws an XA-Resource error right before the first time it throws the client's transaction aborted error. I know that you can get those when your bean tries to access another session. I've seen this error before when running locally and trying to maintain connections to both databases in one method.
Could it be that my singleton properties bean, which accesses both databases, gets into a weird transactional state trying to return a mysql session in one thread and a db2 session in the other?

Categories