This is the command to display information data sheet, I see the declaration it is repeated
CODE 1
public class DBTable {
public List<MdGmail> showGmail() {
Configuration conf = new Configuration().configure("hibernate.cfg.xml");
SessionFactory sf = conf.buildSessionFactory();
currentSession = sf.getCurrentSession();
currentSession.beginTransaction();
return currentSession.createCriteria(MdGmail.class).list();
}
public List<MdBlogger> showBlogger() {
Configuration conf = new Configuration().configure("hibernate.cfg.xml");
SessionFactory sf = conf.buildSessionFactory();
currentSession = sf.getCurrentSession();
currentSession.beginTransaction();
return currentSession.createCriteria(MdBlogger.class).list();
}
}
In my class DBTable,I create a list function to display data table in a database as table gmail,order...
With the first code 1 need to rewrite the declaration for each function, so what I want is to create a connection function for all with code 2
But with the way this declaration it will not update the value was changed in the database
CODE 2
public class DBTable {
private static final Configuration conf = new Configuration().configure("hibernate.cfg.xml");
private static final SessionFactory sf = conf.buildSessionFactory();
private static Session currentSession;
public DBTable() {
currentSession = sf.getCurrentSession();
currentSession.beginTransaction();
}
private static DBTable instance = null;
public static DBTable getInstance() {
if (instance == null) {
instance = new DBTable();
}
return instance;
}
public List<MdGmail> showTableGmail() {
return currentSession.createCriteria(MdGmail.class).list();
}
public List<MdGmail> showTableOrder() {
return currentSession.createCriteria(MdGmail.class).list();
}
}
And if use new additional data function, the connection will be closed and command data showing that my table would stop working.Please help me
public boolean saveOrUpdateGmail(MdGmail table) {
try {
currentSession.saveOrUpdate(table);
currentSession.getTransaction().commit();
return true;
} catch (Exception e) {
currentSession.getTransaction().rollback();
e.printStackTrace();
return false;
}
}
The simplest strategy of using Hibernate is open a session (or get current one), begin a transaction, do request, commit, and close a session for every operation (showTableGmail, saveOrUpdateGmail). So you need to delete all code from DBTable() constructor and do something like this.
Related
I know this question has been asked before however none of the solutions have worked for me.
I am trying to hit a controller to populate an index. The issue arises when I try and search the database for updates.
Here is are the classes I am dealing with:
Configuration:
#Configuration
#EnableTransactionManagement
public class WebApplication implements WebApplicationContextInitializer, ApplicationContextAware {
#Bean(name="dataSource")
public DataSource getDataSource() throws IOException {
InitialContext initialContext = new Context();
return (DataSource) initialContext.lookup("java:comp/env/jdbc/myDataSource");
}
#Bean(name="sessionFactory")
public SessionFactory getSessionFactory() throws IOException {
LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(getDataSource());
sessionBuilder.scanPackages(PropertyUtil.getInstance().getPropertySplitTrimmed("hibernate", "packagesToScan"));
sessionBuilder.addProperties(PropertyUtil.getInstance().getProperties("hibernate"));
return sessionBuilder.buildSessionFactory();
}
#Bean(name="transactionManager")
public HibernateTransactionManager transactionManager() throws IOException {
return new HibernateTransactionManager(getSessionFactory());
}
}
Controller:
#RestController
#Transactional
#RequestMapping("/persons")
public class IndexController {
#Autowired
PersonsDao personsDoa;
private ExecutorService executorService = Executors.newFixedThreadPool(100);
#RequestMapping(value="/index")
public void populateIndex(#DefaultValue("") #RequestParam String name){
...
...
List<Future<Persons>> holder = new ArrayList<>();
for(Persons p : people){
String name = p.name();
Future<Person> f = this.executorService.submit(new Callable<Person>(){
#Override
public Person call() throws Exception {
return personsDao.findByName(name); // <-- Throws error here
}
});
holder.add(f); // process the array later once all threads are finished
}
...
...
}
}
UPDATE: I've updated my Controller according to some suggestions, however I am still receiving the same error
Controller:
#RestController
#Transactional
#RequestMapping("/persons")
public class IndexController {
#Autowired
PersonsDao personsDoa;
private ExecutorService executorService = Executors.newFixedThreadPool(100);
#RequestMapping(value="/index")
public void populateIndex(#DefaultValue("") #RequestParam String name){
...
...
List<Future<Persons>> holder = new ArrayList<>();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(sessionFactory.getCurrentSession())); //<-- THROWS ERROR HERE
for(Persons p : people){
String name = p.name();
Future<Person> f = this.executorService.submit(new Callable<Person>(){
SessionHolder holder = (SessionHolder)TransactionSynchronizationManager.getResources(sessionFactory);
Session session = holder.getSession();
#Override
public Person call() throws Exception {
Transaction t = session.getTransaction();
t.begin();
Persons p = personsDao.findByName(name);
t.commit();
session.flush();
return p;
}
});
holder.add(f); // process the array later once all threads are finished
}
...
...
}
}
Usualy the request thread use only one shared Session, this session is binded at the start of the request, and unbinded at the end of the request, but given you want to use it in another thread, we must :
1_ prevent the session from being closed from the Request Thread.
2_ bind this session to the new thread, to offer the TransactionManager to works with same Session.
First the current Session must not be closed, so if you are using the OpenInViewFilter, you need to add a method before calling the new Thread.
OpenEntityManagerInViewFilter.getCurrent().keepEmfOpen(request);
Then inside the Thread you need to attach the current session.
public void attachThread() {// this must bind the session to this thread.
OpenEntityManagerInViewFilter.getCurrent().registerEmfs(request, session);
}
private boolean registerEmf(String key, ServletRequest request, EntityManagerFactory emf){
if (emf == null)
return true;
if (TransactionSynchronizationManager.hasResource(emf))
return true;
else {
boolean isFirstRequest = true;
WebAsyncManager asyncManager = null;
if (request!=null){
asyncManager = WebAsyncUtils.getAsyncManager(request);
isFirstRequest = !(request instanceof HttpServletRequest) || !isAsyncDispatch((HttpServletRequest) request);
}
if (emf.isOpen())
if (isFirstRequest || !applyEntityManagerBindingInterceptor(asyncManager, key)) {
logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewFilter");
try {
EntityManager em = createEntityManager( emf );
EntityManagerHolder emHolder = new EntityManagerHolder( em );
TransactionSynchronizationManager.bindResource( emf, emHolder );
if (asyncManager!=null)
asyncManager.registerCallableInterceptor( key, new EntityManagerBindingCallableInterceptor( emf, emHolder ) );
return false;
}
catch (PersistenceException ex) {
throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
}
}
}
return true;
}
in case of OpenSessionInViewFilter :
private void openHibernateSessionInView(){
Session session=SessionFactoryUtils.getSession(sessionFactory,true);
SessionHolder holder=new SessionHolder(session);
if (!TransactionSynchronizationManager.hasResource(sessionFactory)) {
TransactionSynchronizationManager.bindResource(sessionFactory,holder);
}
}
private void closeHibernateSessionInView(){
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
SessionHolder sessionHolder=(SessionHolder)TransactionSynchronizationManager.unbindResource(sessionFactory);
if (sessionHolder.getTransaction() != null && !sessionHolder.getTransaction().wasRolledBack() && !sessionHolder.getTransaction().wasCommitted()) {
sessionHolder.getTransaction().commit();
}
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
}
In our application we have an HibernateSessionFactory class, that is opening and closing connections. Everything is okay, but when we are updating data in the database, it doesn't change in our application. Unfortunately, we see old data from the database. How can I fix this?
public class HibernateSessionFactory {
private static final ThreadLocal threadLocal = new ThreadLocal();
private static org.hibernate.SessionFactory sessionFactory;
private static Configuration configuration = new Configuration();
private static ServiceRegistry serviceRegistry;
private static final Logger log = Logger.getLogger(
HibernateSessionFactory.class);
static {
try {
configuration.configure();
serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (Exception e) {
log.error("Error Creating SessionFactory", e);
}
}
private HibernateSessionFactory() {}
public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
session = (sessionFactory != null) ?
sessionFactory.openSession() : null;
threadLocal.set(session);
}
return session;
}
public static void rebuildSessionFactory() {
try {
configuration.configure();
serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (Exception e) {
log.error("Error Creating SessionFactory", e);
}
}
public static void closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
threadLocal.set(null);
if (session != null) {
session.flush();
session.close();
}
}
public static org.hibernate.SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Configuration getConfiguration() {
return configuration;
}
}
.
#SuppressWarnings("unchecked")
public List<Tauthor> getAuthors() throws HibernateException {
log.debug("getting all authors");
Query queryObject = null;
List<Tauthor> authors = null;
Session session = HibernateSessionFactory.getSession();
try {
String queryString = "from Tauthor";
queryObject = session.createQuery(queryString);
authors = queryObject.list();
} catch (HibernateException e) {
log.error("get all authors failed", e);
throw e;
} finally {
HibernateSessionFactory.closeSession();
}
return authors;
}
You haven't shared your code where you write data to the database. Without that, I can think of only a few reasons as to why your data output is old instead of new data:
Your transactions are not being committed.
Hibernate hasn't written to the database at the time of data queried by you.
Hibernate's cache hasn't been updated, which results in query returning old data.
You should verify that data has been written to the Database with a db developer tool and try disabling all hibernate caching to see if the result changes.
In our web application we have a HibernateSessionFactory class, that is opening and closing connections. Everything is okay, but when we are updating data in the database, it doesn't change in our application. Unfortunately, we see old data from the database. How can I fix it?
public class HibernateSessionFactory {
private static final ThreadLocal threadLocal = new ThreadLocal();
private static org.hibernate.SessionFactory sessionFactory;
private static Configuration configuration = new Configuration();
private static ServiceRegistry serviceRegistry;
private static final Logger log = Logger.getLogger(HibernateSessionFactory.class);
static {
try {
configuration.configure();
serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties())
. buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (Exception e) {
log.error("Error Creating SessionFactory",e);
}
}
private HibernateSessionFactory() {
}
public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
session = (sessionFactory != null) ? sessionFactory.openSession()
: null;
threadLocal.set(session);
}
return session;
}
public static void rebuildSessionFactory() {
try {
configuration.configure();
serviceRegistry = new ServiceRegistryBuilder(). applySettings(configuration.getProperties())
.buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (Exception e) {
log.error("Error Creating SessionFactory",e);
}
}
public static void closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
threadLocal.set(null);
if (session != null) {
session.flush();
session.close();
}
}
public static org.hibernate.SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Configuration getConfiguration() {
return configuration;
}
}
Hibernate is a sofisticated and complex framework for building a layer between your program and the database, providing an object oriented model to help object oriented programs in their job.
In order to do this, and to be more performat of course, it creates a cache which stores somehow some of the data coming from the database, or going to the database.
I think that this problem is NOT concerning the connection to the database but rather how you save and retrive data.
I'll try to explain my self better: when you query database to save data you do the following steps:
open session
open transaction
build object
flush and save object in session
commit transaction
close transaction
close session
eg
public Boolean saveNewCliente(Cliente c) {
Session s = getSession();
Transaction t = null;
try {
t = s.beginTransaction();
s.save(c);
s.flush();
t.commit();
s.close();
return true;
} catch (Exception e) {
if (t!=null) t.rollback();
System.out.println(e.getMessage());
return false;
}
finally{
s.close();
}
}
A common pitfall happens when you query database to fetch data, is to leave the part relating the transaction out of your "query" steps. As well for saving data you have to
open session
open transaction
build query / criteria
execute query over session
close transaction
close session
If you don't follow this steps is possible that you have stale data in your application, but not in your database. A check may be to execute your update/save-query and check manually in the database if the data has changed/created. If your application then loads stale data you know you do the fetch-query in the wrong way(without using transaction).
Here's a snipped of example
public Cliente get(Integer id) {
Session s = getSession();
Transaction tx = s.beginTransaction();
try {
System.out.println("get cliente by id");
Cliente res = new Cliente();
res = (Cliente) s.get(Cliente.class, id);
tx.commit();
return res;
} catch (Exception e) {
tx.rollback();
System.out.println(e.getMessage());
return null;
}finally{
s.close();
}
}
If you want to furthermore investigate you can suspend the usage of cache by hibernate, you can do it in the following way, but remember that if data is cached there is a reason ;)
This can be useful as a quick test in order to proceed in discovering if the error is due to wrong query usage.
You should add this to your hibernate config xml
<!-- to disable cache -->
<property name="hibernate.cache.use_second_level_cache">false</property>
<property name="hibernate.cache.use_query_cache">false</property>
I have this simple CDI bean which displays content into JSF page:
#Named("ZonesController")
#ViewScoped
public class Zones implements Serializable
{
#Resource(name = "jdbc/Oracle")
private DataSource ds;
...........
public int countDBRowNum() throws Exception
{
String SqlStatement = null;
if (ds == null)
{
throw new SQLException();
}
Connection conn = ds.getConnection();
if (conn == null)
{
throw new SQLException();
}
PreparedStatement ps = null;
ResultSet resultSet = null;
int count = 0;
try
{
conn.setAutoCommit(false);
boolean committed = false;
try
{
SqlStatement = "SELECT COUNT(1) FROM component x, componentstats y WHERE x.componentstatsid = y.componentstatsid AND y.componenttypeid = 1100";
ps = conn.prepareStatement(SqlStatement);
resultSet = ps.executeQuery();
if (resultSet.next())
{
count = resultSet.getInt(1);
}
conn.commit();
committed = true;
}
finally
{
if (!committed)
{
conn.rollback();
}
}
}
finally
{
ps.close();
conn.close();
}
// Returns total rows in table.
return count;
}
.............
}
I created this JUnit test case which calls the Java method:
public class ZonesTest
{
#BeforeClass
public static void setUpClass() throws Exception
{
try
{
// Create initial context
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.naming.java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES,
"org.apache.naming");
InitialContext ic = new InitialContext();
ic.createSubcontext("java:");
ic.createSubcontext("java:/comp");
ic.createSubcontext("java:/comp/env");
ic.createSubcontext("java:/comp/env/jdbc");
// Construct DataSource
OracleConnectionPoolDataSource ds = new OracleConnectionPoolDataSource();
ds.setURL("jdbc:oracle:thin:#192.168.1.104:1521:oracle");
ds.setUser("admin");
ds.setPassword("qwerty");
ic.bind("java:/comp/env/jdbc/oracle", ds);
}
catch (NamingException ex)
{
//Logger.getLogger(MyDAOTest.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Test
public void testCountDBRowNum() throws Exception
{
Zones instance = new Zones();
int rows = instance.countDBRowNum();
System.out.println(rows);
}
}
I get error at these lines:
if (ds == null)
{
throw new SQLException();
}
How I can solve this problem? I want to use the datasource from the JUnit test during the testing. Can I somehow use the JUnit datasource?
You can make DataSource ds a JavaBean property and set its value on your JUnit test. This way you hide the complexity of the JNDI binding and focus your tests only on the business logic.
Your controller:
#Named("ZonesController")
#ViewScoped
public class Zones implements Serializable
{
#Resource(name = "jdbc/Oracle")
private DataSource ds;
public void setDs(DataSource ds){this.ds=ds;}
public DataSource getDs(){return ds;}
...
}
And you test class:
public class ZonesTest
{
private static OracleConnectionPoolDataSource ds;
#BeforeClass
public static void setUpClass() throws Exception
{
try
{
// Construct DataSource
ds = new OracleConnectionPoolDataSource();
ds.setURL("jdbc:oracle:thin:#192.168.1.104:1521:oracle");
ds.setUser("admin");
ds.setPassword("qwerty");
}
catch (NamingException ex)
{
//Logger.getLogger(MyDAOTest.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Test
public void testCountDBRowNum() throws Exception
{
Zones instance = new Zones();
instance.setDs(ds);
int rows = instance.countDBRowNum();
System.out.println(rows);
}
}
As a side note, following the MVC design pattern I would even decouple the controller business logic from the database connection, so no valid connection is needed to execute the unit tests and your focus is entirely on the behavior of your controller.
If you are using Spring and want to use all Spring beans within your JUnit class you can always use the #RunWith JUnit annotation.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class ZonesTest
{
#Autowire
Zones zones;
// your BeforeClass method here
#Test
public void testCountDBRowNum() throws Exception
{
int rows = zones.countDBRowNum();
System.out.println(rows);
}
}
I have create a dbadapter for delaing with hibernate.Actually my class looks like this..
public class DBAdapter {
private static SessionFactory factory;
private static final ThreadLocal<Session> threadSession = new ThreadLocal();
public static Session OpenConnection() {
if (factory == null) {
factory = new Configuration().configure(
"com/et/hibernatexml/hibernate.cfg.xml")
.buildSessionFactory();
}
Session s = (Session) threadSession.get();
if (s == null)
{
s =factory.openSession();
threadSession.set(s);
}
return s;
}
public List selectQuery(String QueryString)
{ try
{
Session session=OpenConnection();
resultlist = query.list();
}
finally()
{
closeSession();
}
}
public static void closeSession()
{
Session session = (Session) threadSession.get();
threadSession.set(null);
if (session != null && session.isOpen()) {
session.flush();
session.close();
}
}
For getting data from server ,i will do like this..
DBAdapter ob=new DBAdapter();
ob.setParameter("orgId", orgId);
List list=ob.selectQuery(queryvalue);
My doubt is any issue by dealing like this.Especially because SessionFactory is static variable??
You do not want more than one threads to create a session factory. It should be a singleton and is by design thread safe. The easiest way to do this with the code you provided is to use the synchronized keyword on the openConnection() method. However, there is no reason to synchronize the part of the code where you create a session and put it on the ThreadLocal instance as well. A rough solution would be like the following
public class DBAdapter {
private static SessionFactory factory;
private static final ThreadLocal<Session> threadSession = new ThreadLocal<Session>();
private static synchronized SessionFactory getSessionFactory() {
if(factory == null) {
factory = new Configuration().configure("com/et/hibernatexml/hibernate.cfg.xml").buildSessionFactory();
}
return factory;
}
public static Session getSession() {
Session s = (Session) threadSession.get();
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
return s;
}
}