| /* |
| * Copyright 2002-2014 the original author or authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.springframework.orm.jpa.vendor; |
| |
| import java.sql.Connection; |
| import java.sql.SQLException; |
| import javax.persistence.EntityManager; |
| import javax.persistence.PersistenceException; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.openjpa.persistence.FetchPlan; |
| import org.apache.openjpa.persistence.OpenJPAEntityManager; |
| import org.apache.openjpa.persistence.OpenJPAPersistence; |
| import org.apache.openjpa.persistence.jdbc.IsolationLevel; |
| import org.apache.openjpa.persistence.jdbc.JDBCFetchPlan; |
| import org.springframework.jdbc.datasource.ConnectionHandle; |
| import org.springframework.jdbc.datasource.ConnectionHolder; |
| import org.springframework.jdbc.support.JdbcUtils; |
| import org.springframework.orm.jpa.DefaultJpaDialect; |
| import org.springframework.transaction.SavepointManager; |
| import org.springframework.transaction.TransactionDefinition; |
| import org.springframework.transaction.TransactionException; |
| |
| /** |
| * {@link org.springframework.orm.jpa.JpaDialect} implementation for Apache OpenJPA. |
| * Developed and tested against OpenJPA 2.2. |
| * |
| * @author Juergen Hoeller |
| * @author Costin Leau |
| * @since 2.0 |
| */ |
| public class OpenJpaDialect extends DefaultJpaDialect { |
| |
| private static final long serialVersionUID = 2099118508988476959L; |
| |
| @Override |
| public Object beginTransaction(final EntityManager entityManager, final TransactionDefinition definition) |
| throws PersistenceException, SQLException, TransactionException { |
| |
| OpenJPAEntityManager openJpaEntityManager = getOpenJPAEntityManager(entityManager); |
| |
| if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { |
| // Pass custom isolation level on to OpenJPA's JDBCFetchPlan configuration |
| FetchPlan fetchPlan = openJpaEntityManager.getFetchPlan(); |
| if (fetchPlan instanceof JDBCFetchPlan) { |
| IsolationLevel isolation = IsolationLevel.fromConnectionConstant(definition.getIsolationLevel()); |
| ((JDBCFetchPlan) fetchPlan).setIsolation(isolation); |
| } |
| } |
| |
| entityManager.getTransaction().begin(); |
| |
| if (!definition.isReadOnly()) { |
| // Like with EclipseLink, make sure to start the logic transaction early so that other |
| // participants using the connection (such as JdbcTemplate) run in a transaction. |
| openJpaEntityManager.beginStore(); |
| } |
| |
| // Custom implementation for OpenJPA savepoint handling |
| return new OpenJpaTransactionData(openJpaEntityManager); |
| } |
| |
| @Override |
| public ConnectionHandle getJdbcConnection(final EntityManager entityManager, final boolean readOnly) |
| throws PersistenceException, SQLException { |
| |
| return new OpenJpaConnectionHandle(getOpenJPAEntityManager(entityManager)); |
| } |
| |
| /** |
| * Return the OpenJPA-specific variant of {@code EntityManager}. |
| * |
| * @param em the generic {@code EntityManager} instance |
| * @return the OpenJPA-specific variant of {@code EntityManager} |
| */ |
| protected static OpenJPAEntityManager getOpenJPAEntityManager(final EntityManager em) { |
| return OpenJPAPersistence.cast(em); |
| } |
| |
| /** |
| * Transaction data Object exposed from {@code beginTransaction}, |
| * implementing the {@link SavepointManager} interface. |
| */ |
| private static class OpenJpaTransactionData implements SavepointManager { |
| |
| private final OpenJPAEntityManager entityManager; |
| |
| private int savepointCounter = 0; |
| |
| OpenJpaTransactionData(final OpenJPAEntityManager entityManager) { |
| this.entityManager = entityManager; |
| } |
| |
| @Override |
| public Object createSavepoint() throws TransactionException { |
| this.savepointCounter++; |
| String savepointName = ConnectionHolder.SAVEPOINT_NAME_PREFIX + this.savepointCounter; |
| this.entityManager.setSavepoint(savepointName); |
| return savepointName; |
| } |
| |
| @Override |
| public void rollbackToSavepoint(final Object savepoint) throws TransactionException { |
| this.entityManager.rollbackToSavepoint((String) savepoint); |
| } |
| |
| @Override |
| public void releaseSavepoint(final Object savepoint) throws TransactionException { |
| try { |
| this.entityManager.releaseSavepoint((String) savepoint); |
| } catch (Throwable ex) { |
| LogFactory.getLog(OpenJpaTransactionData.class).debug( |
| "Could not explicitly release OpenJPA savepoint", ex); |
| } |
| } |
| } |
| |
| /** |
| * {@link ConnectionHandle} implementation that fetches a new OpenJPA-provided |
| * Connection for every {@code getConnection} call and closes the Connection on |
| * {@code releaseConnection}. This is necessary because OpenJPA requires the |
| * fetched Connection to be closed before continuing EntityManager work. |
| * |
| * @see org.apache.openjpa.persistence.OpenJPAEntityManager#getConnection() |
| */ |
| private static class OpenJpaConnectionHandle implements ConnectionHandle { |
| |
| private final OpenJPAEntityManager entityManager; |
| |
| OpenJpaConnectionHandle(final OpenJPAEntityManager entityManager) { |
| this.entityManager = entityManager; |
| } |
| |
| @Override |
| public Connection getConnection() { |
| return (Connection) this.entityManager.getConnection(); |
| } |
| |
| @Override |
| public void releaseConnection(final Connection con) { |
| JdbcUtils.closeConnection(con); |
| } |
| } |
| } |