001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.orm.jpa;
018:
019: import javax.persistence.EntityManager;
020: import javax.persistence.EntityManagerFactory;
021: import javax.persistence.PersistenceException;
022:
023: import org.springframework.beans.factory.InitializingBean;
024: import org.springframework.dao.support.DataAccessUtils;
025:
026: /**
027: * Base class for JpaTemplate and JpaInterceptor, defining common
028: * properties such as EntityManagerFactory and flushing behavior.
029: *
030: * <p>Not intended to be used directly.
031: * See {@link JpaTemplate} and {@link JpaInterceptor}.
032: *
033: * @author Juergen Hoeller
034: * @since 2.0
035: * @see #setEntityManagerFactory
036: * @see #setEntityManager
037: * @see #setJpaDialect
038: * @see #setFlushEager
039: * @see JpaTemplate
040: * @see JpaInterceptor
041: * @see JpaDialect
042: */
043: public abstract class JpaAccessor extends EntityManagerFactoryAccessor
044: implements InitializingBean {
045:
046: private EntityManager entityManager;
047:
048: private JpaDialect jpaDialect = new DefaultJpaDialect();
049:
050: private boolean flushEager = false;
051:
052: /**
053: * Set the JPA EntityManager to use.
054: */
055: public void setEntityManager(EntityManager entityManager) {
056: this .entityManager = entityManager;
057: }
058:
059: /**
060: * Return the JPA EntityManager to use.
061: */
062: public EntityManager getEntityManager() {
063: return entityManager;
064: }
065:
066: /**
067: * Set the JPA dialect to use for this accessor.
068: * <p>The dialect object can be used to retrieve the underlying JDBC
069: * connection, for example.
070: */
071: public void setJpaDialect(JpaDialect jpaDialect) {
072: this .jpaDialect = (jpaDialect != null ? jpaDialect
073: : new DefaultJpaDialect());
074: }
075:
076: /**
077: * Return the JPA dialect to use for this accessor.
078: * <p>Creates a default one for the specified EntityManagerFactory if none set.
079: */
080: public JpaDialect getJpaDialect() {
081: return this .jpaDialect;
082: }
083:
084: /**
085: * Set if this accessor should flush changes to the database eagerly.
086: * <p>Eager flushing leads to immediate synchronization with the database,
087: * even if in a transaction. This causes inconsistencies to show up and throw
088: * a respective exception immediately, and JDBC access code that participates
089: * in the same transaction will see the changes as the database is already
090: * aware of them then. But the drawbacks are:
091: * <ul>
092: * <li>additional communication roundtrips with the database, instead of a
093: * single batch at transaction commit;
094: * <li>the fact that an actual database rollback is needed if the JPA
095: * transaction rolls back (due to already submitted SQL statements).
096: * </ul>
097: */
098: public void setFlushEager(boolean flushEager) {
099: this .flushEager = flushEager;
100: }
101:
102: /**
103: * Return if this accessor should flush changes to the database eagerly.
104: */
105: public boolean isFlushEager() {
106: return this .flushEager;
107: }
108:
109: /**
110: * Eagerly initialize the JPA dialect, creating a default one
111: * for the specified EntityManagerFactory if none set.
112: */
113: public void afterPropertiesSet() {
114: EntityManagerFactory emf = getEntityManagerFactory();
115: if (emf == null && getEntityManager() == null) {
116: throw new IllegalArgumentException(
117: "entityManagerFactory or entityManager is required");
118: }
119: if (emf instanceof EntityManagerFactoryInfo) {
120: JpaDialect jpaDialect = ((EntityManagerFactoryInfo) emf)
121: .getJpaDialect();
122: if (jpaDialect != null) {
123: setJpaDialect(jpaDialect);
124: }
125: }
126: }
127:
128: /**
129: * Flush the given JPA entity manager if necessary.
130: * @param em the current JPA PersistenceManage
131: * @param existingTransaction if executing within an existing transaction
132: * @throws javax.persistence.PersistenceException in case of JPA flushing errors
133: */
134: protected void flushIfNecessary(EntityManager em,
135: boolean existingTransaction) throws PersistenceException {
136: if (isFlushEager()) {
137: logger.debug("Eagerly flushing JPA entity manager");
138: em.flush();
139: }
140: }
141:
142: /**
143: * Convert the given runtime exception to an appropriate exception from the
144: * <code>org.springframework.dao</code> hierarchy if necessary, or
145: * return the exception itself if it is not persistence related
146: * <p>Default implementation delegates to the JpaDialect.
147: * May be overridden in subclasses.
148: * @param ex runtime exception that occured, which may or may not
149: * be JPA-related
150: * @return the corresponding DataAccessException instance if
151: * wrapping should occur, otherwise the raw exception
152: * @see org.springframework.dao.support.DataAccessUtils#translateIfNecessary
153: */
154: public RuntimeException translateIfNecessary(RuntimeException ex) {
155: return DataAccessUtils
156: .translateIfNecessary(ex, getJpaDialect());
157: }
158:
159: }
|