Spring – Hibernate Annotation – Entity classes in JAR not recognised [XXX is not mapped]

Standard
Spread the love

Spring provides options to integrate many ORM frameworks available in Market and one of them is Hibernate. With the options like configuring SessionFactory and HibernateTemplate, it makes developer’s life smooth to integrate other cross cutting concerns. Amongst all the other features, Spring provides a convenient feature is to autodetect “Annotated” Entity classes from package(s) using helper bean “org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean”. The package(s) name can be injected using setter method “packagesToScan”. Set the value of this property to the package name where you place the Entity classes and you are good to go. No need to add entry for every *-hbm.xml file OR explicitly configuring annotated classes.

But recently I came across a problem, wherein I found that my application’s Entity classes were not detected and when I was querying, hibernate was simply throwing an error as “XXX is not mapped”.

Well after struggling for almost an hour and checking out all the configuration, it was not visible as what was causing the problem [almost clueless 🙁 ]. The next step was (obvious) jump into the Spring code and checking out what went wrong. Aha! that really helped and I found the reason why Spring was not able to detect Entity classes. The “AnnotationSessionFactoryBean” class internally prefixes packages with “classpath*:” protocol before searching them and as stated in Spring documentation due to bug in JDK classloader the underlying classloader used by Spring to load resources is not able to recognise files kept within Jar. (mine were kept inside JAR and not in exploded format.). Though the application deployed was a Web Application which creates XmlWebApplicationContext and internally uses “ServletContextResourcePatternResolver” to resolve classes (Resolves resources insdie JAR file), was simply failing due to the fact of having prefix “classpath*:”

Having no provision to override this behaviour of “AnnotationSessionFactoryBean”, I thought of extending the class and added a method to use prefix for loading resources. Also I had to override the scanPackages method to use custom logic for loading resoruces. To load the resources from JAR you need to use inject path as WEB-INF/lib/*.jar!/com.abc. Note the “!/” in path, Spring makes use of this delimiter to identify the JAR and the resource path. After deploying the change, I found that Spring was now able to autodetect the Entity from JAR.

5 thoughts on “Spring – Hibernate Annotation – Entity classes in JAR not recognised [XXX is not mapped]

  1. Jossemar

    Hello, I am facing the same problem and I wonder if you could send me the code that you implemented.
    Thank you for your attention!

  2. Jossemar

    Hello, I’m trying to implement this treatment but I can not!
    You can send me the implementation that you did?
    Help me too!

    Thanks!

    • Hey Jossemar,
      Sorry couldnt reply you immediately. The code is pretty easy and the logic lies within scanPackages method. If you will see the implementation of “org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean”, the class uses ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX (classpath*:) to resolve classes and this might fail on certain environment. You can check Spring reference documentation and search for “Classpath*: portability”.
      To resolve this issue, I have created new class and overriden quite a few methods, but the most interesting one is scanPackages with following

      @Override
      protected void scanPackages(AnnotationConfiguration config) {
      	super.scanPackages(config);
      	if (this.packagesToScan != null) {
      		try {
      			for (String pkg : this.packagesToScan) {
      				String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
      						+ ClassUtils.convertClassNameToResourcePath(pkg)
      						+ RESOURCE_PATTERN;
      				Resource[] resources = this.resourcePatternResolver
      						.getResources(pattern);
      					if ((resources == null) || (resources.length == 0)) {
      						pattern = fallbackClasspathPrefix
      							+ ClassUtils
      									.convertClassNameToResourcePath(pkg)
      							+ RESOURCE_PATTERN;
      					resources = this.resourcePatternResolver
      							.getResources(pattern);
      					MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(
      							this.resourcePatternResolver);
      					for (Resource resource : resources) {
      						if (resource.isReadable()) {
      							MetadataReader reader = readerFactory
      									.getMetadataReader(resource);
      							String className = reader.getClassMetadata()
      									.getClassName();
      							if (matchesFilter(reader, readerFactory)) {
      								config
      										.addAnnotatedClass(this.resourcePatternResolver
      												.getClassLoader()
      												.loadClass(className));
      							}
      						}
      					}
      				}
      			}
      		} catch (IOException ex) {
      			throw new MappingException(
      					"Failed to scan classpath for unlisted classes", ex);
      		} catch (ClassNotFoundException ex) {
      			throw new MappingException(
      					"Failed to load annotated classes from classpath", ex);
      		}
      	}
      }
      


      Note that the “fallbackClasspathPrefix” variable is property injected in Spring configuration using

      
      	WEB-INF/lib/myappvo*.jar!/
      
      


      Hope this helps.

  3. This might help eventually solve my problem. After for two days, many hours headache. Following the exception, following the trace should be always a good way.

Leave a Reply

Your email address will not be published. Required fields are marked *