/*
 * Copyright 2002-2018 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
 *
 *      https://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.eclipse.persistence.sessions.UnitOfWork;

import org.springframework.jdbc.datasource.ConnectionHandle;
import org.springframework.lang.Nullable;
import org.springframework.orm.jpa.DefaultJpaDialect;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;

JpaDialect implementation for Eclipse Persistence Services (EclipseLink). Developed and tested against EclipseLink 2.7; backwards-compatible with EclipseLink 2.5 and 2.6 at runtime.

By default, this class acquires an early EclipseLink transaction with an early JDBC Connection for non-read-only transactions. This allows for mixing JDBC and JPA/EclipseLink operations in the same transaction, with cross visibility of their impact. If this is not needed, set the "lazyDatabaseTransaction" flag to true or consistently declare all affected transactions as read-only. As of Spring 4.1.2, this will reliably avoid early JDBC Connection retrieval and therefore keep EclipseLink in shared cache mode.

Author:Juergen Hoeller
See Also:
Since:2.5.2
/** * {@link org.springframework.orm.jpa.JpaDialect} implementation for Eclipse * Persistence Services (EclipseLink). Developed and tested against EclipseLink 2.7; * backwards-compatible with EclipseLink 2.5 and 2.6 at runtime. * * <p>By default, this class acquires an early EclipseLink transaction with an early * JDBC Connection for non-read-only transactions. This allows for mixing JDBC and * JPA/EclipseLink operations in the same transaction, with cross visibility of * their impact. If this is not needed, set the "lazyDatabaseTransaction" flag to * {@code true} or consistently declare all affected transactions as read-only. * As of Spring 4.1.2, this will reliably avoid early JDBC Connection retrieval * and therefore keep EclipseLink in shared cache mode. * * @author Juergen Hoeller * @since 2.5.2 * @see #setLazyDatabaseTransaction * @see org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy */
@SuppressWarnings("serial") public class EclipseLinkJpaDialect extends DefaultJpaDialect { private boolean lazyDatabaseTransaction = false;
Set whether to lazily start a database resource transaction within a Spring-managed EclipseLink transaction.

By default, read-only transactions are started lazily but regular non-read-only transactions are started early. This allows for reusing the same JDBC Connection throughout an entire EclipseLink transaction, for enforced isolation and consistent visibility with JDBC access code working on the same DataSource.

Switch this flag to "true" to enforce a lazy database transaction begin even for non-read-only transactions, allowing access to EclipseLink's shared cache and following EclipseLink's connection mode configuration, assuming that isolation and visibility at the JDBC level are less important.

See Also:
  • beginEarlyTransaction.beginEarlyTransaction()
/** * Set whether to lazily start a database resource transaction within a * Spring-managed EclipseLink transaction. * <p>By default, read-only transactions are started lazily but regular * non-read-only transactions are started early. This allows for reusing the * same JDBC Connection throughout an entire EclipseLink transaction, for * enforced isolation and consistent visibility with JDBC access code working * on the same DataSource. * <p>Switch this flag to "true" to enforce a lazy database transaction begin * even for non-read-only transactions, allowing access to EclipseLink's * shared cache and following EclipseLink's connection mode configuration, * assuming that isolation and visibility at the JDBC level are less important. * @see org.eclipse.persistence.sessions.UnitOfWork#beginEarlyTransaction() */
public void setLazyDatabaseTransaction(boolean lazyDatabaseTransaction) { this.lazyDatabaseTransaction = lazyDatabaseTransaction; } @Override @Nullable public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition) throws PersistenceException, SQLException, TransactionException { if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { // Pass custom isolation level on to EclipseLink's DatabaseLogin configuration // (since Spring 4.1.2) UnitOfWork uow = entityManager.unwrap(UnitOfWork.class); uow.getLogin().setTransactionIsolation(definition.getIsolationLevel()); } entityManager.getTransaction().begin(); if (!definition.isReadOnly() && !this.lazyDatabaseTransaction) { // Begin an early transaction to force EclipseLink to get a JDBC Connection // so that Spring can manage transactions with JDBC as well as EclipseLink. entityManager.unwrap(UnitOfWork.class).beginEarlyTransaction(); } return null; } @Override public ConnectionHandle getJdbcConnection(EntityManager entityManager, boolean readOnly) throws PersistenceException, SQLException { // As of Spring 4.1.2, we're using a custom ConnectionHandle for lazy retrieval // of the underlying Connection (allowing for deferred internal transaction begin // within the EclipseLink EntityManager) return new EclipseLinkConnectionHandle(entityManager); }
ConnectionHandle implementation that lazily fetches an EclipseLink-provided Connection on the first getConnection call - which may never come if no application code requests a JDBC Connection. This is useful to defer the early transaction begin that obtaining a JDBC Connection implies within an EclipseLink EntityManager.
/** * {@link ConnectionHandle} implementation that lazily fetches an * EclipseLink-provided Connection on the first {@code getConnection} call - * which may never come if no application code requests a JDBC Connection. * This is useful to defer the early transaction begin that obtaining a * JDBC Connection implies within an EclipseLink EntityManager. */
private static class EclipseLinkConnectionHandle implements ConnectionHandle { private final EntityManager entityManager; @Nullable private Connection connection; public EclipseLinkConnectionHandle(EntityManager entityManager) { this.entityManager = entityManager; } @Override public Connection getConnection() { if (this.connection == null) { this.connection = this.entityManager.unwrap(Connection.class); } return this.connection; } } }