/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.userguide.transactions;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.resource.transaction.spi.TransactionStatus;

import org.junit.Test;

/**
 * @author Vlad Mihalcea
 */
public class TransactionsTest extends BaseEntityManagerFunctionalTestCase {

	@Override
	protected Class<?>[] getAnnotatedClasses() {
		return new Class<?>[] {
			Customer.class
		};
	}

	@Test
	public void jdbc() {
		//tag::transactions-api-jdbc-example[]
		StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
				// "jdbc" is the default, but for explicitness
				.applySetting( AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY, "jdbc" )
				.build();

		Metadata metadata = new MetadataSources( serviceRegistry )
				.addAnnotatedClass( Customer.class )
				.getMetadataBuilder()
				.build();

		SessionFactory sessionFactory = metadata.getSessionFactoryBuilder()
				.build();

		Session session = sessionFactory.openSession();
		try {
			// calls Connection#setAutoCommit( false ) to
			// signal start of transaction
			session.getTransaction().begin();

			session.createQuery( "UPDATE customer set NAME = 'Sir. '||NAME" )
					.executeUpdate();

			// calls Connection#commit(), if an error
			// happens we attempt a rollback
			session.getTransaction().commit();
		}
		catch ( Exception e ) {
			// we may need to rollback depending on
			// where the exception happened
			if ( session.getTransaction().getStatus() == TransactionStatus.ACTIVE
					|| session.getTransaction().getStatus() == TransactionStatus.MARKED_ROLLBACK ) {
				session.getTransaction().rollback();
			}
			// handle the underlying error
		}
		finally {
			session.close();
		}
		//end::transactions-api-jdbc-example[]
	}

	@Test
	public void cmt() {
		//tag::transactions-api-cmt-example[]
		StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
				// "jdbc" is the default, but for explicitness
				.applySetting( AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY, "jta" )
				.build();

		Metadata metadata = new MetadataSources( serviceRegistry )
				.addAnnotatedClass( Customer.class )
				.getMetadataBuilder()
				.build();

		SessionFactory sessionFactory = metadata.getSessionFactoryBuilder()
				.build();

		// Note: depending on the JtaPlatform used and some optional settings,
		// the underlying transactions here will be controlled through either
		// the JTA TransactionManager or UserTransaction

		Session session = sessionFactory.openSession();
		try {
			// Since we are in CMT, a JTA transaction would
			// already have been started.  This call essentially
			// no-ops
			session.getTransaction().begin();

			Number customerCount = (Number) session.createQuery( "select count(c) from Customer c" ).uniqueResult();

			// Since we did not start the transaction ( CMT ),
			// we also will not end it.  This call essentially
			// no-ops in terms of transaction handling.
			session.getTransaction().commit();
		}
		catch ( Exception e ) {
			// again, the rollback call here would no-op (aside from
			// marking the underlying CMT transaction for rollback only).
			if ( session.getTransaction().getStatus() == TransactionStatus.ACTIVE
					|| session.getTransaction().getStatus() == TransactionStatus.MARKED_ROLLBACK ) {
				session.getTransaction().rollback();
			}
			// handle the underlying error
		}
		finally {
			session.close();
		}
		//end::transactions-api-cmt-example[]
	}

	@Test
	public void bmt() {
		//tag::transactions-api-bmt-example[]
		StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
				// "jdbc" is the default, but for explicitness
				.applySetting( AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY, "jta" )
				.build();

		Metadata metadata = new MetadataSources( serviceRegistry )
				.addAnnotatedClass( Customer.class )
				.getMetadataBuilder()
				.build();

		SessionFactory sessionFactory = metadata.getSessionFactoryBuilder()
				.build();

		// Note: depending on the JtaPlatform used and some optional settings,
		// the underlying transactions here will be controlled through either
		// the JTA TransactionManager or UserTransaction

		Session session = sessionFactory.openSession();
		try {
			// Assuming a JTA transaction is not already active,
			// this call the TM/UT begin method.  If a JTA
			// transaction is already active, we remember that
			// the Transaction associated with the Session did
			// not "initiate" the JTA transaction and will later
			// nop-op the commit and rollback calls...
			session.getTransaction().begin();

			session.persist( new Customer(  ) );
			Customer customer = (Customer) session.createQuery( "select c from Customer c" ).uniqueResult();

			// calls TM/UT commit method, assuming we are initiator.
			session.getTransaction().commit();
		}
		catch ( Exception e ) {
			// we may need to rollback depending on
			// where the exception happened
			if ( session.getTransaction().getStatus() == TransactionStatus.ACTIVE
					|| session.getTransaction().getStatus() == TransactionStatus.MARKED_ROLLBACK ) {
				// calls TM/UT commit method, assuming we are initiator;
				// otherwise marks the JTA transaction for rollback only
				session.getTransaction().rollback();
			}
			// handle the underlying error
		}
		finally {
			session.close();
		}
		//end::transactions-api-bmt-example[]
	}

	@Entity(name = "Customer")
	public static class Customer {

		@Id
		@GeneratedValue
		private Long id;

		private String name;

		public Customer() {
		}

		public Customer(String name) {
			this.name = name;
		}

		public Long getId() {
			return id;
		}

		public String getName() {
			return name;
		}
	}
}
