001/* 002 * Copyright 2002-2014 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.orm.jpa.vendor; 018 019import java.sql.Connection; 020import java.sql.SQLException; 021import javax.persistence.EntityManager; 022import javax.persistence.PersistenceException; 023 024import org.apache.commons.logging.LogFactory; 025import org.apache.openjpa.persistence.FetchPlan; 026import org.apache.openjpa.persistence.OpenJPAEntityManager; 027import org.apache.openjpa.persistence.OpenJPAPersistence; 028import org.apache.openjpa.persistence.jdbc.IsolationLevel; 029import org.apache.openjpa.persistence.jdbc.JDBCFetchPlan; 030 031import org.springframework.jdbc.datasource.ConnectionHandle; 032import org.springframework.jdbc.datasource.ConnectionHolder; 033import org.springframework.jdbc.support.JdbcUtils; 034import org.springframework.orm.jpa.DefaultJpaDialect; 035import org.springframework.transaction.SavepointManager; 036import org.springframework.transaction.TransactionDefinition; 037import org.springframework.transaction.TransactionException; 038 039/** 040 * {@link org.springframework.orm.jpa.JpaDialect} implementation for Apache OpenJPA. 041 * Developed and tested against OpenJPA 2.2. 042 * 043 * @author Juergen Hoeller 044 * @author Costin Leau 045 * @since 2.0 046 */ 047@SuppressWarnings("serial") 048public class OpenJpaDialect extends DefaultJpaDialect { 049 050 @Override 051 public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition) 052 throws PersistenceException, SQLException, TransactionException { 053 054 OpenJPAEntityManager openJpaEntityManager = getOpenJPAEntityManager(entityManager); 055 056 if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { 057 // Pass custom isolation level on to OpenJPA's JDBCFetchPlan configuration 058 FetchPlan fetchPlan = openJpaEntityManager.getFetchPlan(); 059 if (fetchPlan instanceof JDBCFetchPlan) { 060 IsolationLevel isolation = IsolationLevel.fromConnectionConstant(definition.getIsolationLevel()); 061 ((JDBCFetchPlan) fetchPlan).setIsolation(isolation); 062 } 063 } 064 065 entityManager.getTransaction().begin(); 066 067 if (!definition.isReadOnly()) { 068 // Like with EclipseLink, make sure to start the logic transaction early so that other 069 // participants using the connection (such as JdbcTemplate) run in a transaction. 070 openJpaEntityManager.beginStore(); 071 } 072 073 // Custom implementation for OpenJPA savepoint handling 074 return new OpenJpaTransactionData(openJpaEntityManager); 075 } 076 077 @Override 078 public ConnectionHandle getJdbcConnection(EntityManager entityManager, boolean readOnly) 079 throws PersistenceException, SQLException { 080 081 return new OpenJpaConnectionHandle(getOpenJPAEntityManager(entityManager)); 082 } 083 084 /** 085 * Return the OpenJPA-specific variant of {@code EntityManager}. 086 * @param em the generic {@code EntityManager} instance 087 * @return the OpenJPA-specific variant of {@code EntityManager} 088 */ 089 protected OpenJPAEntityManager getOpenJPAEntityManager(EntityManager em) { 090 return OpenJPAPersistence.cast(em); 091 } 092 093 094 /** 095 * Transaction data Object exposed from {@code beginTransaction}, 096 * implementing the {@link SavepointManager} interface. 097 */ 098 private static class OpenJpaTransactionData implements SavepointManager { 099 100 private final OpenJPAEntityManager entityManager; 101 102 private int savepointCounter = 0; 103 104 public OpenJpaTransactionData(OpenJPAEntityManager entityManager) { 105 this.entityManager = entityManager; 106 } 107 108 @Override 109 public Object createSavepoint() throws TransactionException { 110 this.savepointCounter++; 111 String savepointName = ConnectionHolder.SAVEPOINT_NAME_PREFIX + this.savepointCounter; 112 this.entityManager.setSavepoint(savepointName); 113 return savepointName; 114 } 115 116 @Override 117 public void rollbackToSavepoint(Object savepoint) throws TransactionException { 118 this.entityManager.rollbackToSavepoint((String) savepoint); 119 } 120 121 @Override 122 public void releaseSavepoint(Object savepoint) throws TransactionException { 123 try { 124 this.entityManager.releaseSavepoint((String) savepoint); 125 } 126 catch (Throwable ex) { 127 LogFactory.getLog(OpenJpaTransactionData.class).debug( 128 "Could not explicitly release OpenJPA savepoint", ex); 129 } 130 } 131 } 132 133 134 /** 135 * {@link ConnectionHandle} implementation that fetches a new OpenJPA-provided 136 * Connection for every {@code getConnection} call and closes the Connection on 137 * {@code releaseConnection}. This is necessary because OpenJPA requires the 138 * fetched Connection to be closed before continuing EntityManager work. 139 * @see org.apache.openjpa.persistence.OpenJPAEntityManager#getConnection() 140 */ 141 private static class OpenJpaConnectionHandle implements ConnectionHandle { 142 143 private final OpenJPAEntityManager entityManager; 144 145 public OpenJpaConnectionHandle(OpenJPAEntityManager entityManager) { 146 this.entityManager = entityManager; 147 } 148 149 @Override 150 public Connection getConnection() { 151 return (Connection) this.entityManager.getConnection(); 152 } 153 154 @Override 155 public void releaseConnection(Connection con) { 156 JdbcUtils.closeConnection(con); 157 } 158 } 159 160}