001/* 002 * Copyright 2012-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 * 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 017package org.springframework.boot.orm.jpa; 018 019import java.net.URL; 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.LinkedHashMap; 023import java.util.Map; 024import java.util.Set; 025 026import javax.sql.DataSource; 027 028import org.springframework.core.task.AsyncTaskExecutor; 029import org.springframework.orm.jpa.JpaVendorAdapter; 030import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 031import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager; 032import org.springframework.util.ClassUtils; 033import org.springframework.util.ObjectUtils; 034import org.springframework.util.StringUtils; 035 036/** 037 * Convenient builder for JPA EntityManagerFactory instances. Collects common 038 * configuration when constructed and then allows you to create one or more 039 * {@link LocalContainerEntityManagerFactoryBean} through a fluent builder pattern. The 040 * most common options are covered in the builder, but you can always manipulate the 041 * product of the builder if you need more control, before returning it from a 042 * {@code @Bean} definition. 043 * 044 * @author Dave Syer 045 * @author Phillip Webb 046 * @author Stephane Nicoll 047 * @since 1.3.0 048 */ 049public class EntityManagerFactoryBuilder { 050 051 private final JpaVendorAdapter jpaVendorAdapter; 052 053 private final PersistenceUnitManager persistenceUnitManager; 054 055 private final Map<String, Object> jpaProperties; 056 057 private final URL persistenceUnitRootLocation; 058 059 private AsyncTaskExecutor bootstrapExecutor; 060 061 /** 062 * Create a new instance passing in the common pieces that will be shared if multiple 063 * EntityManagerFactory instances are created. 064 * @param jpaVendorAdapter a vendor adapter 065 * @param jpaProperties the JPA properties to be passed to the persistence provider 066 * @param persistenceUnitManager optional source of persistence unit information (can 067 * be null) 068 */ 069 public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter, 070 Map<String, ?> jpaProperties, PersistenceUnitManager persistenceUnitManager) { 071 this(jpaVendorAdapter, jpaProperties, persistenceUnitManager, null); 072 } 073 074 /** 075 * Create a new instance passing in the common pieces that will be shared if multiple 076 * EntityManagerFactory instances are created. 077 * @param jpaVendorAdapter a vendor adapter 078 * @param jpaProperties the JPA properties to be passed to the persistence provider 079 * @param persistenceUnitManager optional source of persistence unit information (can 080 * be null) 081 * @param persistenceUnitRootLocation the persistence unit root location to use as a 082 * fallback (can be null) 083 * @since 1.4.1 084 */ 085 public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter, 086 Map<String, ?> jpaProperties, PersistenceUnitManager persistenceUnitManager, 087 URL persistenceUnitRootLocation) { 088 this.jpaVendorAdapter = jpaVendorAdapter; 089 this.persistenceUnitManager = persistenceUnitManager; 090 this.jpaProperties = new LinkedHashMap<>(jpaProperties); 091 this.persistenceUnitRootLocation = persistenceUnitRootLocation; 092 } 093 094 public Builder dataSource(DataSource dataSource) { 095 return new Builder(dataSource); 096 } 097 098 /** 099 * Configure the bootstrap executor to be used by the 100 * {@link LocalContainerEntityManagerFactoryBean}. 101 * @param bootstrapExecutor the executor 102 * @since 2.1.0 103 */ 104 public void setBootstrapExecutor(AsyncTaskExecutor bootstrapExecutor) { 105 this.bootstrapExecutor = bootstrapExecutor; 106 } 107 108 /** 109 * A fluent builder for a LocalContainerEntityManagerFactoryBean. 110 */ 111 public final class Builder { 112 113 private DataSource dataSource; 114 115 private String[] packagesToScan; 116 117 private String persistenceUnit; 118 119 private Map<String, Object> properties = new HashMap<>(); 120 121 private String[] mappingResources; 122 123 private boolean jta; 124 125 private Builder(DataSource dataSource) { 126 this.dataSource = dataSource; 127 } 128 129 /** 130 * The names of packages to scan for {@code @Entity} annotations. 131 * @param packagesToScan packages to scan 132 * @return the builder for fluent usage 133 */ 134 public Builder packages(String... packagesToScan) { 135 this.packagesToScan = packagesToScan; 136 return this; 137 } 138 139 /** 140 * The classes whose packages should be scanned for {@code @Entity} annotations. 141 * @param basePackageClasses the classes to use 142 * @return the builder for fluent usage 143 */ 144 public Builder packages(Class<?>... basePackageClasses) { 145 Set<String> packages = new HashSet<>(); 146 for (Class<?> type : basePackageClasses) { 147 packages.add(ClassUtils.getPackageName(type)); 148 } 149 this.packagesToScan = StringUtils.toStringArray(packages); 150 return this; 151 } 152 153 /** 154 * The name of the persistence unit. If only building one EntityManagerFactory you 155 * can omit this, but if there are more than one in the same application you 156 * should give them distinct names. 157 * @param persistenceUnit the name of the persistence unit 158 * @return the builder for fluent usage 159 */ 160 public Builder persistenceUnit(String persistenceUnit) { 161 this.persistenceUnit = persistenceUnit; 162 return this; 163 } 164 165 /** 166 * Generic properties for standard JPA or vendor-specific configuration. These 167 * properties override any values provided in the constructor. 168 * @param properties the properties to use 169 * @return the builder for fluent usage 170 */ 171 public Builder properties(Map<String, ?> properties) { 172 this.properties.putAll(properties); 173 return this; 174 } 175 176 /** 177 * The mapping resources (equivalent to {@code <mapping-file>} entries in 178 * {@code persistence.xml}) for the persistence unit. 179 * <p> 180 * Note that mapping resources must be relative to the classpath root, e.g. 181 * "META-INF/mappings.xml" or "com/mycompany/repository/mappings.xml", so that 182 * they can be loaded through {@code ClassLoader.getResource()}. 183 * @param mappingResources the mapping resources to use 184 * @return the builder for fluent usage 185 */ 186 public Builder mappingResources(String... mappingResources) { 187 this.mappingResources = mappingResources; 188 return this; 189 } 190 191 /** 192 * Configure if using a JTA {@link DataSource}, i.e. if 193 * {@link LocalContainerEntityManagerFactoryBean#setDataSource(DataSource) 194 * setDataSource} or 195 * {@link LocalContainerEntityManagerFactoryBean#setJtaDataSource(DataSource) 196 * setJtaDataSource} should be called on the 197 * {@link LocalContainerEntityManagerFactoryBean}. 198 * @param jta if the data source is JTA 199 * @return the builder for fluent usage 200 */ 201 public Builder jta(boolean jta) { 202 this.jta = jta; 203 return this; 204 } 205 206 public LocalContainerEntityManagerFactoryBean build() { 207 LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); 208 if (EntityManagerFactoryBuilder.this.persistenceUnitManager != null) { 209 entityManagerFactoryBean.setPersistenceUnitManager( 210 EntityManagerFactoryBuilder.this.persistenceUnitManager); 211 } 212 if (this.persistenceUnit != null) { 213 entityManagerFactoryBean.setPersistenceUnitName(this.persistenceUnit); 214 } 215 entityManagerFactoryBean.setJpaVendorAdapter( 216 EntityManagerFactoryBuilder.this.jpaVendorAdapter); 217 218 if (this.jta) { 219 entityManagerFactoryBean.setJtaDataSource(this.dataSource); 220 } 221 else { 222 entityManagerFactoryBean.setDataSource(this.dataSource); 223 } 224 entityManagerFactoryBean.setPackagesToScan(this.packagesToScan); 225 entityManagerFactoryBean.getJpaPropertyMap() 226 .putAll(EntityManagerFactoryBuilder.this.jpaProperties); 227 entityManagerFactoryBean.getJpaPropertyMap().putAll(this.properties); 228 if (!ObjectUtils.isEmpty(this.mappingResources)) { 229 entityManagerFactoryBean.setMappingResources(this.mappingResources); 230 } 231 URL rootLocation = EntityManagerFactoryBuilder.this.persistenceUnitRootLocation; 232 if (rootLocation != null) { 233 entityManagerFactoryBean 234 .setPersistenceUnitRootLocation(rootLocation.toString()); 235 } 236 if (EntityManagerFactoryBuilder.this.bootstrapExecutor != null) { 237 entityManagerFactoryBean.setBootstrapExecutor( 238 EntityManagerFactoryBuilder.this.bootstrapExecutor); 239 } 240 return entityManagerFactoryBean; 241 } 242 243 } 244 245}