Showing posts with label Hibernate. Show all posts
Showing posts with label Hibernate. Show all posts

19 February 2017

spwrap: Stored Procedure call wrapper

spwrap is a Stored Procedure caller; simply execute stored procedure from java code.
Example:

public interface CustomerDAO {

    @StoredProc("create_customer")
    void createCustomer(@Param(VARCHAR) String firstName, @Param(VARCHAR) String lastName);

    @StoredProc("get_customer")
    Customer getCustomer(@Param(INTEGER) Integer id);   

    @StoredProc("list_customers")
    List<Customer> listCustomers();
}
public class Customer implements TypedOutputParamMapper<Customer>, ResultSetMapper<Customer> {

    private Integer id;
    private String firstName, lastName;

    public Customer() {
    }

    public Customer(Integer id, String firstName, String lastName) {
        super();
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public Integer id() {
        return id;
    }

    public String firstName() {
        return firstName;
    }

    public String lastName() {
        return lastName;
    }

    @Override
    public Customer map(Result<?> result) {
        if (result.isResultSet()) {// for ResultSetMapper
            return new Customer(result.getInt(1), result.getString(2), result.getString(3));
        } else { // for TypedOutputParamMapper
            return new Customer(null, result.getString(1), result.getString(2));
        }
    }

    // for TypedOutputParamMapper
    @Override
    public List<Integer> getTypes() {
        return Arrays.asList(VARCHAR, VARCHAR);
    }
}
DAO dao = new DAO.Builder(dataSource).build();
CustomerDAO customerDao = dao.create(CustomerDAO.class);

customerDao.createCustomer("Abdullah", "Muhammad");
Customer abdullah = customerDao.getCustomer(0);
// ......
Learn more at github: https://github.com/mhewedy/spwrap 

16 July 2012

Using Dynamic Proxies to Manage Transactions to DB (3)

This is the third post and the more mature one in the same topic, Please see :

Post 1: http://m-hewedy.blogspot.com/2010/04/using-dynamic-proxies-to-centralize-jpa.html
Post 2: http://m-hewedy.blogspot.com/2012/06/using-dynamic-proxies-to-centralize.html

The following code is the more mature (but still have a bug see: http://stackoverflow.com/questions/11565022/setting-instance-variables-of-the-proxied-objects-in-the-invocationhandler#comment15299502_11565022 )


please see http://stackoverflow.com/questions/11506159/is-there-a-limit-for-maximum-number-of-concurrent-transactions-in-hibernate

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;


/**
 * http://stackoverflow.com/questions/2587702
 * 
 * @author mohammad_abdullah
 */
public class ServiceProxy implements InvocationHandler {

 private final Object object;
 private final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
 private static final String SESSION_FACTORY_FIELD = "sessionFactory";

 public static final Map ACTIVE_TRANSACTIONS = new HashMap();

 private ServiceProxy(final Object object) {
  this.object = object;
 }

 public static Object newInstance(final Object object) {
  return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new ServiceProxy(object));
 }

 @Override
 public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {

  Object result = null;
  SessionFactory sessionFactory = null;
  boolean isActive = false;
  try {
   if (Modifier.isPublic(method.getModifiers())) {

    sessionFactory = HibernateUtil.getSessionFactory();

    final Field sessionField = this.object.getClass().getSuperclass().getDeclaredField(SESSION_FACTORY_FIELD);
    if (sessionField == null) {
     throw new UPSAdminException("Service Implementation should have field named: \"" + SESSION_FACTORY_FIELD + "\".");
    }
    sessionField.setAccessible(true);
    sessionField.set(this.object, sessionFactory);

    isActive = sessionFactory.getCurrentSession().getTransaction().isActive();

    if (!isActive) {
     this.logger.info("Tnx begin." + " {Method: " + method.getName() + "} {Thread: " + this.threadId() + "}");
     final Transaction newTnx = sessionFactory.getCurrentSession().beginTransaction();
     ACTIVE_TRANSACTIONS.put(this.threadId(), newTnx);
    } else {
     this.logger.info("Joing Active tnx" + " {Method: " + method.getName() + "} {Thread: " + this.threadId() + "}");
    }
    result = method.invoke(this.object, args);

    if (!isActive) {
     ACTIVE_TRANSACTIONS.remove(this.threadId()).commit();
     this.logger.info("Tnx commit" + " {Method: " + method.getName() + "} {Thread: " + this.threadId() + "}");
    }
   } else {
    result = method.invoke(this.object, args);
   }

   return result;

  } catch (final InvocationTargetException _ex) {
   final Throwable cause = _ex.getCause();
   this.logger.severe("Caller Exept: " + cause + " {Method: " + method.getName() + "} {Thread: " + this.threadId() + "}");

   if (!isActive && (sessionFactory.getCurrentSession() != null) && sessionFactory.getCurrentSession().getTransaction().isActive()) {
    ACTIVE_TRANSACTIONS.remove(this.threadId()).rollback();
   }

   if (cause instanceof HibernateException) {
    this.logger.severe("Hibernate Error. Rollback. {Method: " + method.getName() + "} {Thread: " + this.threadId() + "}");
    throw new DBException(cause.getCause().getMessage());

   } else if (cause instanceof SetRollbackException) {
    this.logger.severe("Tnx marked rollback. {Method: " + method.getName() + "} {Thread: " + this.threadId() + "}");
    return result;

   } else {
    this.logger.severe("Error in Business Method : " + method + ". Rollbacked Back." + " Thread: " + this.threadId() + "}");
    throw cause;
   }
  } catch (final Exception ex) {
   this.logger.severe("Error in Proxy code :" + ex + " {Mehtod :" + method + "} {Thread: " + this.threadId() + "}");

   if (!isActive && (sessionFactory.getCurrentSession() != null) && sessionFactory.getCurrentSession().getTransaction().isActive()) {
    ACTIVE_TRANSACTIONS.remove(this.threadId()).rollback();
   }

   if (ex instanceof HibernateException) {
    throw new DBException(ex.getCause().getMessage());
   }

   throw ex;
  }
 }

 private long threadId() {
  return Thread.currentThread().getId();
 }
}

28 June 2012

Using Dynamic Proxies to centralize Transaction Management (2)

I've talked about Using Dynamic Proxies to centralize JPA code In an earlier time, and here's a refined version of the Proxy class:

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.logging.Logger;

import org.hibernate.HibernateException;
import org.hibernate.Session;

import org.daz.DBException;
import org.daz.SetRollbackException;
import org.daz.HibernateUtil;

/**
 * http://stackoverflow.com/questions/2587702
 * 
 * @author mohammad_abdullah
 */
public class ServiceProxy implements InvocationHandler {

    private Object object;
    private Logger logger = Logger.getLogger(this.getClass().getSimpleName());
    private static final String SESSION_FIELD = "session";

    private ServiceProxy(Object object) {
        this.object = object;
    }

    public static Object newInstance(Object object) {
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new ServiceProxy(object));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Object result = null;
        Session session = null;
        boolean joined = false;
        try {
            if (Modifier.isPublic(method.getModifiers())) {

                session = HibernateUtil.getSessionFactory().getCurrentSession();

                Field sessionField = object.getClass().getSuperclass().getDeclaredField(SESSION_FIELD);
                if (sessionField == null)
                    throw new RuntimeException("Service Implementation should have field named: \"" + SESSION_FIELD + "\".");
                sessionField.setAccessible(true);
                sessionField.set(object, session);

                if (session.getTransaction().isActive()) {
                    joined = true;
                    logger.info("Using Already Active transaction");
                } else {
                    logger.info("Transaction Began");
                    session.beginTransaction();
                }
                result = method.invoke(object, args);

                if (!joined) {
                    session.getTransaction().commit();
                    logger.info("Transaction Commited");
                }
            } else {
                result = method.invoke(object, args);
            }

            return result;

        } catch (InvocationTargetException _ex) {
            Throwable cause = _ex.getCause();
            logger.severe("Caller Exception: " + cause);

            if (!joined && session != null && session.getTransaction().isActive())
                session.getTransaction().rollback();

            if (cause instanceof HibernateException) {
                logger.severe("Hibernate Error. Rollbacked Back.");
                throw new DBException(cause.getCause().getMessage());

            /*SetRollbackException is a user-defined exception that can be thrown from Business method to mark transaction for rollback */
            } else if (cause instanceof SetRollbackException) {        
                logger.severe("Transaction marked for Rollback. Rollbacked Back.");
                return result;

            } else {
                logger.severe("Error in Business Method : " + method + ". Rollbacked Back.");
                throw cause;
            }
        } catch (Exception ex) {
            logger.severe("Error in Proxy code :" + ex);

            if (!joined && session != null && session.getTransaction().isActive())
                session.getTransaction().rollback();

            if (ex instanceof HibernateException)
                throw new DBException(ex.getCause().getMessage());

            throw ex;
        }
    }
}

10 December 2010

Handling 2 one-to-many relationships in JPQL

Hi,

I had the following scenario:

I've Entry Department that is has one-to-many relationship with Employee and also had a one-to-many relationship with (say) Section.
And I need to Join these three entities, I've used the following query :
select d from Department d join d.employees e join d.sections s

And the generated SQL was correct.

02 March 2010

Hibernate : "failed to lazily initialize a collection", Reasons and Solutions

Al Salamo Alykom

Most of us usually focuses the most famous hibernate problem "failed to lazily initialize a collection", So here I'll try to say Why it appears and How to solve.

First Why it appears, When you map a one-to-many relation ship, the default load mode is lazy loading, and this is the best choice thing because Hibernate will not gonna load a collection of objects in memory until you use them.
So, If you try to access this collection within the same session you get the parent object, it will works fine.

So the problem happens when you try to access a collection of some object outside the initiating session.

Second, How to solve.
Hibernates uses proxies to refer to collections (and objects as well) when returned as reference to another object.
Suppose we have tow objects, Country and Region, each Region can contain many countries, So the following code will return an exception:

......
session.beginTransaction();
Region region = (Region) session.get(Region.class, 1);
session.getTransaction().commit();
session.close();
printCountryList(region.getCountries()); // << The problem will happen here

So, Should I keep the session opened across client requests, or what should I do?

There are two solutions and I tried both and both works for me.
Hibernate team says that whevener you accessed any method on the proxy, we will load the (real) object (collection ) for you.
So, In case of the previous example, all you have to do is to call any method on the collection within the session, say size():

......
session.beginTransaction();
Region region = (Region) session.get(Region.class, 1);
region.getCountries().size(); // will make hibernate initialize the collection for you instead of the proxy
session.getTransaction().commit();
session.close();
printCountryList(region.getCountries()); // << Will works very fine

Or you can use Hibernate.initialize() to initialize the collection from the proxy :

session.beginTransaction();
Region region = (Region) session.get(Region.class, 1);
Hibernate.initialize(region.getCountries()); // will make hibernate initialize the collection for you instead of the proxy
session.getTransaction().commit();
session.close();
printCountryList(region.getCountries()); // << Will works very fine


That's all, please if you have such the problem, try any of these ways and tell me know about your feedback.
Thanks.

10 March 2009

Managing user Transaction in Hibernate

In the name of ALLAH,

Today we will talk about an important topic in doing Databases, User Transactions.

There are two types of transactions found in any Database application, Database Transactions and User(Application) Transactions. 

Database Transactions are transactions that executed as a serious of SQL statements sequentially, once completed, the transaction commits ( or rolled back ).

example of DB Transaction when a Bank Client what to transfer funds form his first account to his second account, that will be done in two stages, first decrement his first account with the amount he specified, second, add to his second account the amount he wishing transfer.

In such scenario, if one stage fails, the other should fail.

So if we implement these two stages as ordinary two SQL update statements, the DB couldn't ensure that the two will be committed or rolled back together, so the solution in Transactions.

In DB Transaction, the Transaction either committed or rolled back as a whole.

The Database Management System (DBMS) itself who ensure the Atomicity, Consistency , Isolation and Durability of these transactions (ACID).

So what ACID ??

ACID is a certain operations granted  to be executed by the database against DB Transaction.

Atomicity means : single unit of work ( either the transaction committed or rolled back at all )

Isolation means : no changes cannot made to the data used in the transaction, the transaction is isolated from other transaction and database changes (i.e. if another user want to change the data under transaction, he can't ).

Consistency means : the transaction shouldn't leave the data in non-consistent state.

Durability means : once the transaction committed, all changes during it, make persisted in the database.

But, what happened if the Transaction should be span multiple User Sessions ( not hibernate Session interface ) ?

In another meaning, suppose we have an Enterprise-based web application (Web Application), the user makes changes that are related and occurs in subsequent request-response cycle.

What if the user should change his bank account in 2 or more requests to the server ??

in this case, we cannot relay on Database Transactions, So here's the User (Application) Transaction arises .

User Transactions

We will take about user transaction in a context of a simple example, suppose we have an e-commerce web application, we have more that one administrator that can change the price of the products according to some criteria, what happens when two Administrators want to change the price of one product at the same time, the first administrator will change the price based on the original price say he changed the price of a laptop computer from 4000 L.E to 3800 L.E. And the other administrator change the price from also 4000L.E. ( he still didn't see the new changes of the database ) to 4200 L.E. ??

the second administrator thought that he changes the price from 4000 to 4200, but really he will change the price from 3800 ( that changed be the other administrator will he opened the changing page and not yet decided to change the price ) to 4200, the Owner never permit that a product be increased in price by 400 L.E. A once !!

So we have three choices as system implementers ..

1)      override the first administrator by the second administrator and set the price to 4200 ( violate the business rules )

2)      not to allow the second administrator to commit his changes saying that another administrator at the same time has already changed the price

3)      not to allow the second administrator to commit his changes and showing him the changes made by the other administrator.

Solution 2 & 3 are more reasonable, in first solution, hibernate have nothing to do, neither you.

In the second and third solution Hibernate come and get a solution to such a concurrency problems by using “Managed Versioning” .

In managed versioning, you will add a new column in the database called version ( for example ), and add a new property for the object to be persisted called version.

So far so good, the new thing is to add a tag called version immediately after the identifier property that reassemble :

< version name="version" column="version" >

that is all about, when hibernate is going to update the column it will issue a SQL statement looks like this :

UPDATE products p SET p.price = “4200” AND version = 1

WHERE p.id = 123  AND version = 0

if another transaction updated that column at the same time, the version id shouldn't contain the value 0, so the update statement will never executed and hibernate throws exception of type org.hibernate.StaleObjectStateException