-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 
Author Message
 Post subject: Autodetect hibernate annotated classes in classpath
PostPosted: Wed Oct 19, 2005 2:46 pm 
Newbie

Joined: Wed Oct 19, 2005 1:56 pm
Posts: 2
Hello,

I have been using the Hibernate annotations beta6 and noticed that I had to configure each class I annotated using the standard

<mapping class="package.classname"/>

mechanism. The thing is since I no longer have hbm files I thought it would be nice if I could pass in a pattern that hibernate could use to detect the classes I annotated. This way I would be able to modularize my code more easily by simple specifing a pattern that would search the classpath for my files. In my case I keep all of my entity classes in a "model" package.

I found that I was able to do this by simple modifing the org.hibernate.cfg.AnnotationConfiguration class. I mostly changed the
parseMappingElement(Element subelement, String name) method but had to make a few additions to the addAnnotatedClass(Class persistentClass) method as well to make sure any classes that made it into the
Quote:
annotatedClasses
List where annotated correctly.

Here is the code for the relevant methods

protected void parseMappingElement(Element subelement, String name)
{
Attribute rsrc = subelement.attribute("resource");
Attribute file = subelement.attribute("file");
Attribute jar = subelement.attribute("jar");
Attribute pckg = subelement.attribute("package");
Attribute clazz = subelement.attribute("class");
if (rsrc != null) {
log.debug(name + "<-" + rsrc);
addResource(rsrc.getValue());
}
else if (jar != null) {
log.debug(name + "<-" + jar);
addJar(new File(jar.getValue()));
}
else if (file != null) {
log.debug(name + "<-" + file);
addFile(file.getValue());
}
else if (pckg != null) {
log.debug(name + "<-" + pckg);
addPackage(pckg.getValue());
}
else if (clazz != null) {
log.debug(name + "<-" + clazz);
Class loadedClass = null;

List<Class> annotatedClazzes = new ArrayList<Class>();
try {
// clazzValue should always end in ".class" for pattern matching
// for annotated files
String clazzValue = clazz.getValue();

try {
// Try the class as a regular class name first
loadedClass = ReflectHelper.classForName(clazzValue);
annotatedClazzes.add(loadedClass);
}
catch (Exception ex) {
// If it fails check to see if it is a pattern
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

// See if the BASE_PACKAGE property was set to enable the
// shortcut for getting a classes package
String basePackage = getProperties().getProperty(
BASE_PACKAGE);

// Get resources matching the class pattern
Resource[] resources = resolver.getResources(clazzValue);

// Check each resource returned from the
// PathMatchingResourcePatternResolver
String finalClassName;
File f;
for (Resource r : resources) {
finalClassName = null;
f = r.getFile();

// Make sure the resource is a file
if (!f.isDirectory()) {
// Get absolute path
String className = f.getAbsolutePath();
// Remove file extension and replace all '\' with
// '.'
int dotIndex = className.lastIndexOf('.');
if (dotIndex != -1) {
className = className.substring(0, dotIndex);
className = className.replaceAll("\\\\", ".");
}

// If basePackage is specified use shortcut to find
// package name
if (basePackage != null) {
dotIndex = className.indexOf(basePackage);

if (dotIndex != -1) {
finalClassName = className
.substring(dotIndex);
}
}
// otherwise do it the hard way removing one folder
// at a time until a full class name is found
// or there are no more folders
else {
Class c = null;
dotIndex = 0;
// check to see if the className is a real class
while ((c == null) && (dotIndex != -1)) {
try {
className = className
.substring(dotIndex);
ReflectHelper.classForName(className);
}
// if it is not found remove a folder from
// the front of the className and continue
catch (ClassNotFoundException cnf) {
dotIndex = className.indexOf(".") + 1;
}
}
finalClassName = className;
}
}

// Get Class object if a class name was found for the
// resource
if (finalClassName != null) {
loadedClass = ReflectHelper
.classForName(finalClassName);
annotatedClazzes.add(loadedClass);
}
}
}

// Loop through all of the classes found
for (Class annotatedClass : annotatedClazzes) {
addAnnotatedClass(annotatedClass);
}
}
catch (ClassNotFoundException cnf) {
throw new MappingException(
"Unable to load class declared as <mapping class=\""
+ clazz.getValue()
+ "\"/> in the configuration:", cnf);
}
catch (NoClassDefFoundError ncdf) {
throw new MappingException(
"Unable to load class declared as <mapping class=\""
+ clazz.getValue()
+ "\"/> in the configuration:", ncdf);
}
// Handle IOException's that may occur while retrieving resources
catch (IOException io) {
throw new MappingException(
"Unable to load class declared as <mapping class=\""
+ clazz.getValue()
+ "\"/> in the configuration:", io);
}
}
else {
throw new MappingException(
"<mapping> element in configuration specifies no attributes");
}
}


public AnnotationConfiguration addAnnotatedClass(Class persistentClass)
throws MappingException {
// Make sure the persistentClass is not an interface and
// make sure the Class is not already in the annotatedClasses List
// before adding it
if ((!persistentClass.isInterface())
&& (!annotatedClasses.contains(persistentClass))) {

// Make sure the provided class has one of the required annotations
boolean hasRequiredAnnotation = false;
for (Class annotation : REQUIRED_ANNOTATIONS) {
hasRequiredAnnotation = (hasRequiredAnnotation || persistentClass
.isAnnotationPresent(annotation));
}

if (hasRequiredAnnotation) {
try {
if (persistentClass.isAnnotationPresent(Entity.class)) {
annotatedClassEntities.put(persistentClass.getName(),
persistentClass);
}
annotatedClasses.add(persistentClass);
}
catch (MappingException me) {
log.error("Could not compile the mapping annotations", me);
throw me;
}
}
}
return this;
}

I also had to add a couple instance variables.
public static final Class[] REQUIRED_ANNOTATIONS = new Class[] {
Entity.class, Embeddable.class, EmbeddableSuperclass.class};
public static final String BASE_PACKAGE = "base.package";

The REQUIRED_ANNOTATIONS array is used to specify the annotations a class can have. If a class doesn't have one of these it isn't processed.

The BASE_PACKAGE variable is a new property I needed. It can be used to speed up the computation of a classes package name from a resource path. It is not required to be specified in the hibernate.cfg.xml file, however.

Here is an example hibernate.cfg.xml using the new mechanism.

<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.url">
@hibernate.connection.url@
</property>
<property name="show_sql">
@hibernate.show_sql@
</property>
<property name="dialect">
@hibernate.dialect@
</property>
<property name="query.factory_class">
org.hibernate.hql.classic.ClassicQueryTranslatorFactory
</property>
<property name="hibernate.connection.username">
@hibernate.connection.username@
</property>
<property name="hibernate.connection.password">
@hibernate.connection.password@
</property>
<property name="hibernate.connection.driver_class">
@hibernate.connection.driver_class@
</property>
<property name="hibernate.default_schema">
@hibernate.connection.username@
</property>

<property name="base.package">org</property>
<mapping class="classpath*:**/*.class" />
</session-factory>
</hibernate-configuration>

Notice the base.package property is set to the first package folder and
that the class mapping specifies an Ant path instead of a specific class name.

I am currently working with Spring also and so I imported to classes

org.springframework.core.io.Resource;
org.springframework.core.io.support.PathMatchingResourcePatternResolver;

Resource is returned by the PathMatchingResourcePatternResolver class. The PathMatchingResourcePatternResolver class is kind of a utility class almost and did the job of retrieving all of the resources in the classpath if an Ant pattern was entered instead of a specific class. This dependency could probably be removed if another replacement was found to do the pattern matching. PathMatchingResourcePatternResolver uses the org.springframework.util.AntPathMatcher to do the pattern matching.

I have been using this for a couple of days now and it has worked beautifully and it would be great if this kind of functionality could be available in a future release of Hibernate's annotations.

Thanks to the Annotation team for there great work.

_________________
Josh


Top
 Profile  
 
 Post subject: Re: Autodetect hibernate annotated classes in classpath
PostPosted: Mon Oct 24, 2005 6:19 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
Actually, a similar feature (ie autodiscovery) is possible using Hibernate Entity Manager via a JAR packaging. It seems more clean to me esp since it's the standard way to do so in EJB3.

swanjr wrote:
Thanks to the Annotation team for there great work.


hehe, thanks. I'll forward to my 143 coworkers ;-P

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 24, 2005 9:23 am 
Newbie

Joined: Wed Oct 19, 2005 1:56 pm
Posts: 2
Thanks for letting me know. I missed that when I looked at Entity Manager. It would be nice to use a cleaner method of detection.

_________________
Josh


Top
 Profile  
 
 Post subject: Re: Autodetect hibernate annotated classes in classpath
PostPosted: Tue Apr 04, 2006 11:00 am 
Newbie

Joined: Fri Mar 24, 2006 9:31 am
Posts: 10
Location: Berlin, Germany
emmanuel wrote:
Actually, a similar feature (ie autodiscovery) is possible using Hibernate Entity Manager via a JAR packaging. It seems more clean to me esp since it's the standard way to do so in EJB3.


Can you give me a hint how to use autodiscovery with the hibernate entity manager ? I didn't find the keyword in the documentation.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Apr 16, 2006 11:38 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
it is activated out of the box. Check http://www.hibernate.org/hib_docs/entitymanager/reference/en/html_single/#setup-configuration

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 02, 2006 5:55 pm 
Beginner
Beginner

Joined: Wed Feb 04, 2004 5:21 pm
Posts: 37
yay more configuration files! :(


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 07, 2006 9:30 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
hvendelbo wrote:
yay more configuration files! :(

What are you talking about?! You have one single configuration file that point to your datasource.

_________________
Emmanuel


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.