Platinum Partner
java,sql,frameworks,persistence

Automatically Mapping Annotated Hibernate Classes With Spring

Doesn't it annoy you when you have to add each class individually to the Spring config when you are dealing with annotated classes? I bet it annoys you even more when you have to refactor a package and Eclipse doesn't refactor the package inside the xml file. I have your solution and it won't cost you anything.

First you need to write your XML just like mine.

 

<!-- add dynamic factory here --><bean id="sessionFactory"class="com.package.spring.AutomatedAnnotationSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="automaticAnnotatedPackages"><list><value>com.package.domain</value><value>com.package.domain.view</value></list></property><property name="annotatedPackages"><list><value>com.package.domain</value><value>com.package.domain.view</value></list></property><!-- annotated source files --><!-- <property name="annotatedClasses"><list><value>example.Account</value><value>example.AccountDetail</value><value>example.Employee</value></list></property> --><property name="hibernateProperties"><props><prop key="hibernate.generate_statistics">true</prop><prop key="hibernate.dialect">${db.dialect}</prop><prop key="hibernate.cache.provider_class">${cache.provider}</prop><!-- <prop key="hibernate.cache.provider_class">org.hibernate.cache.TreeCacheProvider</prop> --><prop key="hibernate.show_sql">false</prop><!-- <prop key="hibernate.hbm2ddl.auto">create</prop> --><prop key="hibernate.validator.apply_to_ddl">true</prop><!-- <prop key="hibernate.validator.autoregister_listeners">true</prop><prop key="hibernate.validator.apply_to_ddl">true</prop><prop key="hibernate.cache.use_query_cache">true</prop> --><!-- <prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</prop> --></props></property></bean>


You'll notice really quick that you need a new class as well.

 

public class AutomatedAnnotationSessionFactoryBean extends      LocalSessionFactoryBean{   private Class[] annotatedClasses;   private String[] annotatedPackages;   private String[] automaticAnnotatedPackages;   private static Log log = LogFactory         .getLog(AutomatedAnnotationSessionFactoryBean.class);   public String[] getAutomaticAnnotatedPackages()   {      return automaticAnnotatedPackages;   }   public void setAutomaticAnnotatedPackages(String[] annotatedPackages)   {      automaticAnnotatedPackages = annotatedPackages;   }   public AutomatedAnnotationSessionFactoryBean()   {      setConfigurationClass(AnnotationConfiguration.class);   }   public void setConfigurationClass(Class configurationClass)   {      if (configurationClass == null            || !AnnotationConfiguration.class                  .isAssignableFrom(configurationClass))      {         throw new IllegalArgumentException(               "AnnotationSessionFactoryBean only supports AnnotationConfiguration or subclasses");      }      super.setConfigurationClass(configurationClass);   }   /**    * Specify annotated classes, for which mappings will be read from    * class-level JDK 1.5+ annotation metadata.    *     * @see org.hibernate.cfg.AnnotationConfiguration#addAnnotatedClass(Class)    */   public void setAnnotatedClasses(Class[] annotatedClasses)   {      this.annotatedClasses = annotatedClasses;   }   /**    * Specify the names of annotated packages, for which package-level JDK 1.5+    * annotation metadata will be read.    *     * @see org.hibernate.cfg.AnnotationConfiguration#addPackage(String)    */   public void setAnnotatedPackages(String[] annotatedPackages)   {      this.annotatedPackages = annotatedPackages;   }   /**    * Reads metadata from annotated classes and packages into the    * AnnotationConfiguration instance.    * <p>    * Calls <code>postProcessAnnotationConfiguration</code> afterwards, to give    * subclasses the chance to perform custom post-processing.    *     * @see #postProcessAnnotationConfiguration    */   protected final void postProcessConfiguration(Configuration config)         throws HibernateException   {      AnnotationConfiguration annConfig = (AnnotationConfiguration) config;      if (this.annotatedClasses != null)      {         for (int i = 0; i < this.annotatedClasses.length; i++)         {            annConfig.addAnnotatedClass(this.annotatedClasses[i]);         }      }      if (this.annotatedPackages != null)      {         for (int i = 0; i < this.annotatedPackages.length; i++)         {            annConfig.addPackage(this.annotatedPackages[i]);         }      }      try      {         if (this.automaticAnnotatedPackages != null)         {            for (int i = 0; i < this.automaticAnnotatedPackages.length; i++)            {               List<String> classList = ResourceLocator.getClassesInPackage(                     automaticAnnotatedPackages[i], new ArrayList<String>()                     {                     }, false);               for (String clazz : classList)               {                  log.info("Found a Class: " + clazz);                  Class thisClass = Class.forName(clazz);                  if (thisClass.isAnnotationPresent(Hibernate.class))                  {                     log.debug("Adding Mapped Package - CLASS: " + clazz);                     annConfig.addAnnotatedClass(thisClass);                     addMetaData(thisClass);                  }               }            }         }      }      catch (Exception e)      {         throw new HibernateException(e);      }      // Perform custom post-processing in subclasses.      postProcessAnnotationConfiguration(annConfig);   }   private static void addMetaData(Class clazz) throws Exception   {      EntityMetaData metaData = new EntityMetaDataImpl();      Method[] methods = clazz.getMethods();      for (int i = 0; i < methods.length; i++)      {         Method method = methods[i];         log.debug("Adding method: " + method.getName());         metaData.addMethod(method.getName(), method.getReturnType());         if (method.isAnnotationPresent(FriendlyName.class))         {            metaData.setFriendlyName(method.getName());         }      }      if (clazz.isAnnotationPresent(Table.class))      {         Table table = (Table) clazz.getAnnotation(Table.class);         metaData.setTableName(table.name());      }      if (clazz.isAnnotationPresent(Entity.class))      {         Entity entity = (Entity) clazz.getAnnotation(Entity.class);         if (entity.name() != null && !StringUtil.isEmpty(entity.name()))            metaData.setEntityName(entity.name());         else            metaData.setEntityName(clazz.getSimpleName());      }      if (clazz.isAnnotationPresent(Auditable.class))         metaData.setAuditable(true);      if (clazz.isAnnotationPresent(Cache.class))         metaData.setCache(true);      if (clazz.isAnnotationPresent(DefaultSort.class))      {         String[] fields = ((DefaultSort) clazz               .getAnnotation(DefaultSort.class)).fields();         SortDirection[] directions = ((DefaultSort) clazz               .getAnnotation(DefaultSort.class)).directions();         Set<ExtOrder> sorts = new LinkedHashSet<ExtOrder>();         for (int i = 0; i < fields.length; i++)         {            sorts.add(new ExtOrder(fields[i], directions[i], true));         }         metaData.setDefaultSort(sorts);      }      if (clazz.isAnnotationPresent(Proxy.class))      {         Proxy proxy = (Proxy) clazz.getAnnotation(Proxy.class);         metaData.setLazy(proxy.lazy());      }      log.debug("Adding meta data for Class: " + clazz);      SpringLoader.addEntityMetaData(clazz, metaData);   }   /**    * To be implemented by subclasses that want to to perform custom    * post-processing of the AnnotationConfiguration object after this    * FactoryBean performed its default initialization.    *     * @param config    *           the current AnnotationConfiguration object    * @throws HibernateException    *            in case of Hibernate initialization errors    */   protected void postProcessAnnotationConfiguration(         AnnotationConfiguration config) throws HibernateException   {   }   public SessionFactory getCurrentSessionFactory()   {      return getSessionFactory();   }   public Map<String, EntityMetaData> getEntityMetaData()   {      return SpringLoader.getEntityMetaData();   }}

The property that you want to pay attention to in the configuration file is automaticAnnotatedPackages. It does all the magic. My custom class will traverse the packages listed and look for the @Hibernate annotation. This is an annotation that you will have to create. You can name it anything, but you just have to look for it. You find it, map your classes in the class and you are off and running. No more having to list each class individually.

From http://www.jeviathon.com

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}