以前一直使用着hibernate,一直不知道entityManager的作用,这次搭建的项目采用的orm框架我采取的是以jpa规范得以实现。
不喜欢讲太多,直接看配置
我喜欢将配置尽可能地写在properties中,而不是在xml中直接写上值,虽然效果一样,但是写在properties中更有一种控制感
1 hibernate.jdbc.fetch_size=50 2 hibernate.jdbc.batch_size=30 3 hibernate.sessionFactory.class=org.springframework.orm.hibernate4.LocalSessionFactoryBean 4 hibernate.dialect=org.hibernate.dialect.MySQLDialect 5 hibernate.show_sql=true 6 hibernate.format_sql=false; 7 hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext 8 hibernate.query.substitutions=true 1,false 0 9 hibernate.default_batch_fetch_size=16 10 hibernate.max_fetch_depth=2 11 hibernate.generate_statistics=true 12 hibernate.bytecode.use_reflection_optimizer=true 13 hibernate.cache.use_second_level_cache=true 14 hibernate.cache.use_query_cache=true 15 hibernate.cache.region.factory_class=org.hibernate.cache.internal.RegionFactoryInitiator 16 hibernate.cache.use_structured_entries=true
接下来是熟悉的applicationContext-orm.xml
1 <beans:bean id="entityManagerFactory" 2 class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 3 <beans:property name="dataSource" ref="dataSource" /> 4 <beans:property name="persistenceXmlLocation" value="classpath:persistence.xml" /> 5 <beans:property name="persistenceUnitName" value="persistenceUnit" /> 6 <beans:property name="jpaVendorAdapter"> 7 <beans:bean 8 class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 9 <beans:property name="showSql" value="true" /> 10 <beans:property name="generateDdl" value="false" /> 11 </beans:bean> 12 </beans:property> 13 <beans:property name="jpaProperties"> 14 <beans:props> 15 <beans:prop key="hibernate.dialect">${hibernate.dialect}</beans:prop> 16 <beans:prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy 17 </beans:prop> 18 <beans:prop key="hibernate.cache.use_second_level_cache">${hibernate.cache.use_second_level_cache} 19 </beans:prop> 20 <beans:prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory_class} 21 </beans:prop> 22 <beans:prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache} 23 </beans:prop> 24 <beans:prop key="hibernate.jdbc.fetch_size">${hibernate.jdbc.fetch_size}</beans:prop> 25 <beans:prop key="hibernate.jdbc.batch_size">${hibernate.jdbc.batch_size}</beans:prop> 26 <beans:prop key="hibernate.connection.isolation">2</beans:prop> 27 <beans:prop key="javax.persistence.validation.mode">none</beans:prop> 28 <beans:prop key="hibernate.search.default.directory_provider">org.hibernate.search.store.FSDirectoryProvider 29 </beans:prop> 30 <beans:prop key="hibernate.search.default.indexBase">${java.io.tmpdir}/${system.project_name}/index 31 </beans:prop> 32 </beans:props> 33 </beans:property> 34 </beans:bean>
以前我也一直是从网上copy出一份直接写上来,直到那天出了一个奇葩的基础框架的问题,让我不得不从配置上去找问题,抛开百度,抛开谷歌,只剩下source code.
下面一层一层地拨开一个它的真面目。
首先是dataSource:
1 private final DefaultPersistenceUnitManager internalPersistenceUnitManager = 2 new DefaultPersistenceUnitManager(); 3 4 5 /** 6 * Specify the JDBC DataSource that the JPA persistence provider is supposed 7 * to use for accessing the database. This is an alternative to keeping the 8 * JDBC configuration in {@code persistence.xml}, passing in a Spring-managed 9 * DataSource instead. 10 * <p>In JPA speak, a DataSource passed in here will be used as "nonJtaDataSource" 11 * on the PersistenceUnitInfo passed to the PersistenceProvider, as well as 12 * overriding data source configuration in {@code persistence.xml} (if any). 13 * Note that this variant typically works for JTA transaction management as well; 14 * if it does not, consider using the explicit {@link #setJtaDataSource} instead. 15 * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b> 16 * @see javax.persistence.spi.PersistenceUnitInfo#getNonJtaDataSource() 17 * @see #setPersistenceUnitManager 18 */ 19 public void setDataSource(DataSource dataSource) { 20 this.internalPersistenceUnitManager.setDataSourceLookup(new SingleDataSourceLookup(dataSource)); 21 this.internalPersistenceUnitManager.setDefaultDataSource(dataSource); 22 }
dataSource在SingleDataSourceLookup中进行了非空处理之后,就作为了默认持久单元管理的数据池,参与了jpa的持久化流程了。
1 /** 2 * Create a new instance of the {@link SingleDataSourceLookup} class. 3 * @param dataSource the single {@link DataSource} to wrap 4 */ 5 public SingleDataSourceLookup(DataSource dataSource) { 6 Assert.notNull(dataSource, "DataSource must not be null"); 7 this.dataSource = dataSource; 8 }
接下来是persistenceXmlLocation,这个.......这么简单我也不想说啥了
1 /** 2 * Set the location of the {@code persistence.xml} file 3 * we want to use. This is a Spring resource location. 4 * <p>Default is "classpath:META-INF/persistence.xml". 5 * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b> 6 * @param persistenceXmlLocation a Spring resource String 7 * identifying the location of the {@code persistence.xml} file 8 * that this LocalContainerEntityManagerFactoryBean should parse 9 * @see #setPersistenceUnitManager 10 */ 11 public void setPersistenceXmlLocation(String persistenceXmlLocation) { 12 this.internalPersistenceUnitManager.setPersistenceXmlLocation(persistenceXmlLocation); 13 }
因为连接信息在dataSource中已经有了,所以我的persistence.xml是一个很简单的文件
后续再讲解persistence.xml的作用
1 <?xml version="1.0" encoding="UTF-8"?> 2 <persistence xmlns="http://java.sun.com/xml/ns/persistence" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 5 http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" 6 version="2.0"> 7 8 <persistence-unit name="persistenceUnit" 9 transaction-type="RESOURCE_LOCAL"></persistence-unit> 10 11 </persistence>
然后是persistenceUnitName,一样一样的
1 /** 2 * Uses the specified persistence unit name as the name of the default 3 * persistence unit, if applicable. 4 * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b> 5 * @see DefaultPersistenceUnitManager#setDefaultPersistenceUnitName 6 */ 7 @Override 8 public void setPersistenceUnitName(String persistenceUnitName) { 9 super.setPersistenceUnitName(persistenceUnitName); 10 this.internalPersistenceUnitManager.setDefaultPersistenceUnitName(persistenceUnitName); 11 }
由于jpa只是一套规范,而没有具体的实现,因此必不可少地需要给这个规范接上实现,这个就是常见的适配器,jpa的适配器就是jpaVendorAdapter,我选用的是HibernateJpaVendorAdapter。
至于这个适配接入和showSql和generateDdl,嗯。。。。。。好奇的童鞋打开看看吧,
1 public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter 2 3 public abstract class AbstractJpaVendorAdapter implements JpaVendorAdapter 4 5 /** 6 * Specify the JpaVendorAdapter implementation for the desired JPA provider, 7 * if any. This will initialize appropriate defaults for the given provider, 8 * such as persistence provider class and JpaDialect, unless locally 9 * overridden in this FactoryBean. 10 */ 11 public void setJpaVendorAdapter(JpaVendorAdapter jpaVendorAdapter) { 12 this.jpaVendorAdapter = jpaVendorAdapter; 13 }
1 /** 2 * Set whether to generate DDL after the EntityManagerFactory has been initialized, 3 * creating/updating all relevant tables. 4 * <p>Note that the exact semantics of this flag depend on the underlying 5 * persistence provider. For any more advanced needs, specify the appropriate 6 * vendor-specific settings as "jpaProperties". 7 * @see org.springframework.orm.jpa.AbstractEntityManagerFactoryBean#setJpaProperties 8 */ 9 public void setGenerateDdl(boolean generateDdl) { 10 this.generateDdl = generateDdl; 11 }
最后一个配置是:jpaProperties,
1 /** 2 * Specify JPA properties, to be passed into 3 * {@code Persistence.createEntityManagerFactory} (if any). 4 * <p>Can be populated with a String "value" (parsed via PropertiesEditor) or a 5 * "props" element in XML bean definitions. 6 * @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map) 7 * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map) 8 */ 9 public void setJpaProperties(Properties jpaProperties) { 10 CollectionUtils.mergePropertiesIntoMap(jpaProperties, this.jpaPropertyMap); 11 }
spring会将这个jpaProperties的map重新封装给jpaPropertyMap
该类中还有一个方法,看方法名称是执行完上面的方法将执行这个方法,中间没看清怎么跑的,总之,debug时,它进来了
1 @Override 2 public final void afterPropertiesSet() throws PersistenceException { 3 if (this.jpaVendorAdapter != null) { 4 if (this.persistenceProvider == null) { 5 this.persistenceProvider = this.jpaVendorAdapter.getPersistenceProvider(); 6 } 7 Map<String, ?> vendorPropertyMap = this.jpaVendorAdapter.getJpaPropertyMap(); 8 if (vendorPropertyMap != null) { 9 for (Map.Entry<String, ?> entry : vendorPropertyMap.entrySet()) { 10 if (!this.jpaPropertyMap.containsKey(entry.getKey())) { 11 this.jpaPropertyMap.put(entry.getKey(), entry.getValue()); 12 } 13 } 14 } 15 if (this.entityManagerFactoryInterface == null) { 16 this.entityManagerFactoryInterface = this.jpaVendorAdapter.getEntityManagerFactoryInterface(); 17 if (!ClassUtils.isVisible(this.entityManagerFactoryInterface, this.beanClassLoader)) { 18 this.entityManagerFactoryInterface = EntityManagerFactory.class; 19 } 20 } 21 if (this.entityManagerInterface == null) { 22 this.entityManagerInterface = this.jpaVendorAdapter.getEntityManagerInterface(); 23 if (!ClassUtils.isVisible(this.entityManagerInterface, this.beanClassLoader)) { 24 this.entityManagerInterface = EntityManager.class; 25 } 26 } 27 if (this.jpaDialect == null) { 28 this.jpaDialect = this.jpaVendorAdapter.getJpaDialect(); 29 } 30 } 31 32 this.nativeEntityManagerFactory = createNativeEntityManagerFactory(); 33 if (this.nativeEntityManagerFactory == null) { 34 throw new IllegalStateException( 35 "JPA PersistenceProvider returned null EntityManagerFactory - check your JPA provider setup!"); 36 } 37 if (this.jpaVendorAdapter != null) { 38 this.jpaVendorAdapter.postProcessEntityManagerFactory(this.nativeEntityManagerFactory); 39 } 40 41 // Wrap the EntityManagerFactory in a factory implementing all its interfaces. 42 // This allows interception of createEntityManager methods to return an 43 // application-managed EntityManager proxy that automatically joins 44 // existing transactions. 45 this.entityManagerFactory = createEntityManagerFactoryProxy(this.nativeEntityManagerFactory); 46 }
首先jpaPropertyMap会将适配器中有且自己没有的键值对占为己有。
接着它会调用适配器,将自己的entityManageFactory和entityManage指向hibernate的地址。