001/*
002 * Copyright 2002-2018 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;
018
019import java.util.HashMap;
020import java.util.Map;
021import java.util.Properties;
022
023import javax.persistence.EntityManager;
024import javax.persistence.EntityManagerFactory;
025
026import org.apache.commons.logging.Log;
027import org.apache.commons.logging.LogFactory;
028
029import org.springframework.beans.BeansException;
030import org.springframework.beans.factory.BeanFactory;
031import org.springframework.beans.factory.BeanFactoryAware;
032import org.springframework.beans.factory.ListableBeanFactory;
033import org.springframework.lang.Nullable;
034import org.springframework.util.Assert;
035import org.springframework.util.CollectionUtils;
036
037/**
038 * Base class for any class that needs to access a JPA {@link EntityManagerFactory},
039 * usually in order to obtain a JPA {@link EntityManager}. Defines common properties.
040 *
041 * @author Juergen Hoeller
042 * @since 2.0
043 * @see EntityManagerFactoryUtils
044 */
045public abstract class EntityManagerFactoryAccessor implements BeanFactoryAware {
046
047        /** Logger available to subclasses. */
048        protected final Log logger = LogFactory.getLog(getClass());
049
050        @Nullable
051        private EntityManagerFactory entityManagerFactory;
052
053        @Nullable
054        private String persistenceUnitName;
055
056        private final Map<String, Object> jpaPropertyMap = new HashMap<>();
057
058
059        /**
060         * Set the JPA EntityManagerFactory that should be used to create
061         * EntityManagers.
062         * @see javax.persistence.EntityManagerFactory#createEntityManager()
063         * @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
064         */
065        public void setEntityManagerFactory(@Nullable EntityManagerFactory emf) {
066                this.entityManagerFactory = emf;
067        }
068
069        /**
070         * Return the JPA EntityManagerFactory that should be used to create
071         * EntityManagers.
072         */
073        @Nullable
074        public EntityManagerFactory getEntityManagerFactory() {
075                return this.entityManagerFactory;
076        }
077
078        /**
079         * Obtain the EntityManagerFactory for actual use.
080         * @return the EntityManagerFactory (never {@code null})
081         * @throws IllegalStateException in case of no EntityManagerFactory set
082         * @since 5.0
083         */
084        protected final EntityManagerFactory obtainEntityManagerFactory() {
085                EntityManagerFactory emf = getEntityManagerFactory();
086                Assert.state(emf != null, "No EntityManagerFactory set");
087                return emf;
088        }
089
090        /**
091         * Set the name of the persistence unit to access the EntityManagerFactory for.
092         * <p>This is an alternative to specifying the EntityManagerFactory by direct reference,
093         * resolving it by its persistence unit name instead. If no EntityManagerFactory and
094         * no persistence unit name have been specified, a default EntityManagerFactory will
095         * be retrieved through finding a single unique bean of type EntityManagerFactory.
096         * @see #setEntityManagerFactory
097         */
098        public void setPersistenceUnitName(@Nullable String persistenceUnitName) {
099                this.persistenceUnitName = persistenceUnitName;
100        }
101
102        /**
103         * Return the name of the persistence unit to access the EntityManagerFactory for, if any.
104         */
105        @Nullable
106        public String getPersistenceUnitName() {
107                return this.persistenceUnitName;
108        }
109
110        /**
111         * Specify JPA properties, to be passed into
112         * {@code EntityManagerFactory.createEntityManager(Map)} (if any).
113         * <p>Can be populated with a String "value" (parsed via PropertiesEditor)
114         * or a "props" element in XML bean definitions.
115         * @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
116         */
117        public void setJpaProperties(Properties jpaProperties) {
118                CollectionUtils.mergePropertiesIntoMap(jpaProperties, this.jpaPropertyMap);
119        }
120
121        /**
122         * Specify JPA properties as a Map, to be passed into
123         * {@code EntityManagerFactory.createEntityManager(Map)} (if any).
124         * <p>Can be populated with a "map" or "props" element in XML bean definitions.
125         * @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
126         */
127        public void setJpaPropertyMap(@Nullable Map<String, Object> jpaProperties) {
128                if (jpaProperties != null) {
129                        this.jpaPropertyMap.putAll(jpaProperties);
130                }
131        }
132
133        /**
134         * Allow Map access to the JPA properties to be passed to the persistence
135         * provider, with the option to add or override specific entries.
136         * <p>Useful for specifying entries directly, for example via "jpaPropertyMap[myKey]".
137         */
138        public Map<String, Object> getJpaPropertyMap() {
139                return this.jpaPropertyMap;
140        }
141
142        /**
143         * Retrieves an EntityManagerFactory by persistence unit name, if none set explicitly.
144         * Falls back to a default EntityManagerFactory bean if no persistence unit specified.
145         * @see #setPersistenceUnitName
146         */
147        @Override
148        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
149                if (getEntityManagerFactory() == null) {
150                        if (!(beanFactory instanceof ListableBeanFactory)) {
151                                throw new IllegalStateException("Cannot retrieve EntityManagerFactory by persistence unit name " +
152                                                "in a non-listable BeanFactory: " + beanFactory);
153                        }
154                        ListableBeanFactory lbf = (ListableBeanFactory) beanFactory;
155                        setEntityManagerFactory(EntityManagerFactoryUtils.findEntityManagerFactory(lbf, getPersistenceUnitName()));
156                }
157        }
158
159
160        /**
161         * Obtain a new EntityManager from this accessor's EntityManagerFactory.
162         * <p>Can be overridden in subclasses to create specific EntityManager variants.
163         * @return a new EntityManager
164         * @throws IllegalStateException if this accessor is not configured with an EntityManagerFactory
165         * @see javax.persistence.EntityManagerFactory#createEntityManager()
166         * @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
167         */
168        protected EntityManager createEntityManager() throws IllegalStateException {
169                EntityManagerFactory emf = obtainEntityManagerFactory();
170                Map<String, Object> properties = getJpaPropertyMap();
171                return (!CollectionUtils.isEmpty(properties) ? emf.createEntityManager(properties) : emf.createEntityManager());
172        }
173
174        /**
175         * Obtain the transactional EntityManager for this accessor's EntityManagerFactory, if any.
176         * @return the transactional EntityManager, or {@code null} if none
177         * @throws IllegalStateException if this accessor is not configured with an EntityManagerFactory
178         * @see EntityManagerFactoryUtils#getTransactionalEntityManager(javax.persistence.EntityManagerFactory)
179         * @see EntityManagerFactoryUtils#getTransactionalEntityManager(javax.persistence.EntityManagerFactory, java.util.Map)
180         */
181        @Nullable
182        protected EntityManager getTransactionalEntityManager() throws IllegalStateException{
183                EntityManagerFactory emf = obtainEntityManagerFactory();
184                return EntityManagerFactoryUtils.getTransactionalEntityManager(emf, getJpaPropertyMap());
185        }
186
187}