views:

29

answers:

2

Hi,

I'm writing a JSF 2.0 application with Hibernate and Postgresql database. My problem is quite poor understanding on how to handle session with Hibernate when inserting data into more than one table at a time. I have a method savePerson. The method is called as many times as there are addresses submitted by a user but the object Person is only created once. The method savePerson then calls the method addAddress. The code looks like this:

public Person savePerson(Person p, String address) {

        Transaction tx = null;
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();

        if (p == null) {
             = new Person();

            try {
                tx = session.beginTransaction();
                session.save(p);
                tx.commit();
            } catch (RuntimeException e) {
                if (tx != null && tx.isActive()) {
                    try {

                        tx.rollback();
                    } catch (HibernateException e1) {
                    }

                    throw e;
                }
            }
        }

        int id = p.getId();
        addAddress(4, id, address);

        return p;
    }

And the method addAddress:

public void addAddress(int table_id, int row_id, String address) {

        Transaction tx = null;
        session = HibernateUtil.getSessionFactory().openSession();
        Address a = new Address(table_id, row_id, address);

        try {
            tx = session.beginTransaction();
            session.save(a);
            tx.commit();
        } catch (RuntimeException e) {
            System.out.println("first try");

            if (tx != null && tx.isActive()) {
                System.out.println("first try if");
                try {
                    System.out.println("first try if try");

                    tx.rollback();
                } catch (HibernateException e1) {

                }
                // throw again the first exception
                throw e;
            }
            throw e;
        }

But I get the following error:

org.hibernate.TransactionException: Transaction not successfully started

So what is the proper way of opening and closing session when inserting the data into more than one table at a time?

Best Regards, sass.

+1  A: 

A common practice would be to create a Filter which opens the Hibernate session before processing the request/response and commits and closes the session after processing the request/response. Finally map this Filter on <servlet-name> of FacesServlet. This is called Open Session In View or Open Session Per Request.

Spring provides such a filter out of the box, the OpenSessionInViewFilter, which is useful for the case that you're already using Spring or are considering to use it. If you don't (want to) do Spring, you can find here another concrete kickoff examples which you could just copypaste and finetune/expand.

Another practices are listed in this MyFaces FAQ (although MyFaces 1.2 targeted, this applies as good on Mojarra 1.x and 2.x).

BalusC
Thanks, BalusC. Does it mean that I can't leave it the way it is with some code improvements?
sass
You could, but it's only unnecessary code clutter and duplication and prone to problems in lazy loading and persisting objects with a relationship during a single request.
BalusC
I think that the transactional part is a key part of the question and using the OSIV pattern would "solve" it as a kind of side effect. +1 anyway for the suggestion.
Pascal Thivent
@Pascal: Thanks. The "major flaw in design" already made me to +1 yours :)
BalusC
+1  A: 

A TransactionException indicates that a transaction could not be begun, committed or rolled back. But providing the full stacktrace would probably helpful to fully understand the problem.

That being said, while you can create multiple (serial) Transaction from a single Session, there is IMO a major flaw in your design. If you need to insert both the Person and the Address, you should very likely do it in a single unit of work. With your current approach, adding an Address can fail and leave a freshly inserted Person in an inconsistent state.

So what is the proper way of opening and closing session when inserting the data into more than one table at a time?

Do it in a single transaction.

And if you want to remove the transaction management from your methods, you should consider using the Open Session In View pattern, as suggested by BalusC. Unless you want finer grained transaction control of course.

Resources

Pascal Thivent
Thanks, Pascal. The problem is that I'm creating the Person object once but often need to add the address many times. can I still do it in one transaction?
sass
If you're doing this in a single request, then you should certainly do it in a single transaction.
BalusC
@sass Create the Person (and `save` it) only if required. I don't see the problem.
Pascal Thivent