001/* 002 * Copyright 2002-2014 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.hibernate3.annotation; 018 019import java.io.IOException; 020import java.util.Set; 021import java.util.TreeSet; 022import javax.persistence.Embeddable; 023import javax.persistence.Entity; 024import javax.persistence.MappedSuperclass; 025 026import org.hibernate.HibernateException; 027import org.hibernate.MappingException; 028import org.hibernate.cfg.Configuration; 029 030import org.springframework.context.ResourceLoaderAware; 031import org.springframework.core.io.Resource; 032import org.springframework.core.io.ResourceLoader; 033import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 034import org.springframework.core.io.support.ResourcePatternResolver; 035import org.springframework.core.io.support.ResourcePatternUtils; 036import org.springframework.core.type.classreading.CachingMetadataReaderFactory; 037import org.springframework.core.type.classreading.MetadataReader; 038import org.springframework.core.type.classreading.MetadataReaderFactory; 039import org.springframework.core.type.filter.AnnotationTypeFilter; 040import org.springframework.core.type.filter.TypeFilter; 041import org.springframework.util.ClassUtils; 042 043/** 044 * Subclass of Spring's standard LocalSessionFactoryBean for Hibernate, 045 * supporting annotation metadata for mappings. 046 * 047 * <p>Example for an AnnotationSessionFactoryBean bean definition: 048 * 049 * <pre class="code"> 050 * <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> 051 * <property name="dataSource" ref="dataSource"/> 052 * <property name="annotatedClasses"> 053 * <list> 054 * <value>test.package.Foo</value> 055 * <value>test.package.Bar</value> 056 * </list> 057 * </property> 058 * </bean></pre> 059 * 060 * Or when using classpath scanning for autodetection of entity classes: 061 * 062 * <pre class="code"> 063 * <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> 064 * <property name="dataSource" ref="dataSource"/> 065 * <property name="packagesToScan" value="test.package"/> 066 * </bean></pre> 067 * 068 * <p>Requires Hibernate 3.6.x, as of Spring 4.0. 069 * 070 * @author Juergen Hoeller 071 * @since 1.2.2 072 * @see #setDataSource 073 * @see #setHibernateProperties 074 * @see #setAnnotatedClasses 075 * @see #setAnnotatedPackages 076 * @deprecated as of Spring 4.3, in favor of Hibernate 4.x/5.x 077 */ 078@Deprecated 079public class AnnotationSessionFactoryBean extends org.springframework.orm.hibernate3.LocalSessionFactoryBean 080 implements ResourceLoaderAware { 081 082 private static final String RESOURCE_PATTERN = "/**/*.class"; 083 084 private static final String PACKAGE_INFO_SUFFIX = ".package-info"; 085 086 087 private Class<?>[] annotatedClasses; 088 089 private String[] annotatedPackages; 090 091 private String[] packagesToScan; 092 093 private TypeFilter[] entityTypeFilters = new TypeFilter[] { 094 new AnnotationTypeFilter(Entity.class, false), 095 new AnnotationTypeFilter(Embeddable.class, false), 096 new AnnotationTypeFilter(MappedSuperclass.class, false), 097 new AnnotationTypeFilter(org.hibernate.annotations.Entity.class, false)}; 098 099 private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); 100 101 102 /** 103 * Specify annotated classes, for which mappings will be read from 104 * class-level annotation metadata. 105 * @see org.hibernate.cfg.Configuration#addAnnotatedClass(Class) 106 */ 107 public void setAnnotatedClasses(Class<?>... annotatedClasses) { 108 this.annotatedClasses = annotatedClasses; 109 } 110 111 /** 112 * Specify the names of annotated packages, for which package-level 113 * annotation metadata will be read. 114 * @see org.hibernate.cfg.Configuration#addPackage(String) 115 */ 116 public void setAnnotatedPackages(String... annotatedPackages) { 117 this.annotatedPackages = annotatedPackages; 118 } 119 120 /** 121 * Specify packages to search using Spring-based scanning for entity classes in 122 * the classpath. This is an alternative to listing annotated classes explicitly. 123 * <p>Default is none. Specify packages to search for autodetection of your entity 124 * classes in the classpath. This is analogous to Spring's component-scan feature 125 * ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}). 126 */ 127 public void setPackagesToScan(String... packagesToScan) { 128 this.packagesToScan = packagesToScan; 129 } 130 131 /** 132 * Specify custom type filters for Spring-based scanning for entity classes. 133 * <p>Default is to search all specified packages for classes annotated with 134 * {@code @javax.persistence.Entity}, {@code @javax.persistence.Embeddable} 135 * or {@code @javax.persistence.MappedSuperclass}, as well as for 136 * Hibernate's special {@code @org.hibernate.annotations.Entity}. 137 * @see #setPackagesToScan 138 */ 139 public void setEntityTypeFilters(TypeFilter... entityTypeFilters) { 140 this.entityTypeFilters = entityTypeFilters; 141 } 142 143 @Override 144 public void setResourceLoader(ResourceLoader resourceLoader) { 145 this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); 146 } 147 148 149 /** 150 * Reads metadata from annotated classes and packages into the 151 * AnnotationConfiguration instance. 152 */ 153 @Override 154 protected void postProcessMappings(Configuration config) throws HibernateException { 155 if (this.annotatedClasses != null) { 156 for (Class<?> annotatedClass : this.annotatedClasses) { 157 config.addAnnotatedClass(annotatedClass); 158 } 159 } 160 if (this.annotatedPackages != null) { 161 for (String annotatedPackage : this.annotatedPackages) { 162 config.addPackage(annotatedPackage); 163 } 164 } 165 scanPackages(config); 166 } 167 168 /** 169 * Perform Spring-based scanning for entity classes. 170 * @see #setPackagesToScan 171 */ 172 protected void scanPackages(Configuration config) { 173 if (this.packagesToScan != null) { 174 Set<String> classNames = new TreeSet<String>(); 175 Set<String> packageNames = new TreeSet<String>(); 176 try { 177 for (String pkg : this.packagesToScan) { 178 String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + 179 ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN; 180 Resource[] resources = this.resourcePatternResolver.getResources(pattern); 181 MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver); 182 for (Resource resource : resources) { 183 if (resource.isReadable()) { 184 MetadataReader reader = readerFactory.getMetadataReader(resource); 185 String className = reader.getClassMetadata().getClassName(); 186 if (matchesEntityTypeFilter(reader, readerFactory)) { 187 classNames.add(className); 188 } 189 else if (className.endsWith(PACKAGE_INFO_SUFFIX)) { 190 packageNames.add(className.substring(0, className.length() - PACKAGE_INFO_SUFFIX.length())); 191 } 192 } 193 } 194 } 195 } 196 catch (IOException ex) { 197 throw new MappingException("Failed to scan classpath for unlisted classes", ex); 198 } 199 try { 200 for (String className : classNames) { 201 config.addAnnotatedClass(this.resourcePatternResolver.getClassLoader().loadClass(className)); 202 } 203 for (String packageName : packageNames) { 204 config.addPackage(packageName); 205 } 206 } 207 catch (ClassNotFoundException ex) { 208 throw new MappingException("Failed to load annotated classes from classpath", ex); 209 } 210 } 211 } 212 213 /** 214 * Check whether any of the configured entity type filters matches 215 * the current class descriptor contained in the metadata reader. 216 */ 217 private boolean matchesEntityTypeFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException { 218 if (this.entityTypeFilters != null) { 219 for (TypeFilter filter : this.entityTypeFilters) { 220 if (filter.match(reader, readerFactory)) { 221 return true; 222 } 223 } 224 } 225 return false; 226 } 227 228}