Grails:自动发现JPA注释的域类

支持将JPA批注(例如@Entity )添加到2.0中grails-app/domain Groovy类中的问题已得到解决。 这是由于对使用AST转换将大多数GORM方法添加到域类字节码而不是在运行时通过元编程将它们添加到元类所做的更改。 有一种解决方法–将类放在src/groovy (或将它们写在Java中,然后将它们放在src/java )。

尽管这会增加维护的麻烦,因为通过在grails-app/domain中会自动发现类,但是不会扫描src/groovysrc/java中带注释的类,因此必须在grails-app/conf/hibernate/hibernate.cfg.xml明确列出它们grails-app/conf/hibernate/hibernate.cfg.xml 。 我们做注释Groovy和Java类的Spring bean注释的能力等类似的支持的东西@Component并有一个可选属性grails.spring.bean.packagesConfig.groovy ,可以包含一个或多个包名称进行搜索。 我们配置一个Spring扫描器来查找带注释的类,并自动将它们注册为bean。 这就是我们为JPA注释的src/groovysrc/java类所需要的。

事实证明,有一个Spring类可以做到这一点, org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean 。 它扩展了标准的SessionFactory工厂bean类org.springframework.orm.hibernate3.LocalSessionFactoryBean并添加了对要使用的类名的显式列表以及要扫描的包的列表的支持。 不幸的是,Grails工厂bean类org.codehaus.groovy.grails.orm.hibernate.ConfigurableLocalSessionFactoryBean也扩展了LocalSessionFactoryBean因此,如果将应用程序配置为使用AnnotationSessionFactoryBean ,则会从ConfigurableLocalSessionFactoryBean丢失很多重要功能。 因此,这是ConfigurableLocalSessionFactoryBean的子类,该子类借鉴了AnnotationSessionFactoryBean的有用注释支持,并且可以在Grails应用程序中使用:

package com.burtbeckwith.grails.jpa;

import java.io.IOException;

import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;

import org.codehaus.groovy.grails.orm.hibernate.ConfigurableLocalSessionFactoryBean;
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.cfg.Configuration;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;

/**
 * Based on org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean.
 * @author Burt Beckwith
 */
public class AnnotationConfigurableLocalSessionFactoryBean extends ConfigurableLocalSessionFactoryBean implements ResourceLoaderAware {

   private static final String RESOURCE_PATTERN = '/**/*.class';

   private Class<?>[] annotatedClasses;
   private String[] annotatedPackages;
   private String[] packagesToScan;

   private TypeFilter[] entityTypeFilters = new TypeFilter[] {
         new AnnotationTypeFilter(Entity.class, false),
         new AnnotationTypeFilter(Embeddable.class, false),
         new AnnotationTypeFilter(MappedSuperclass.class, false),
         new AnnotationTypeFilter(org.hibernate.annotations.Entity.class, false)};

   private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

   public AnnotationConfigurableLocalSessionFactoryBean() {
      setConfigurationClass(GrailsAnnotationConfiguration.class);
   }

   public void setAnnotatedClasses(Class<?>[] annotatedClasses) {
      this.annotatedClasses = annotatedClasses;
   }

   public void setAnnotatedPackages(String[] annotatedPackages) {
      this.annotatedPackages = annotatedPackages;
   }

   public void setPackagesToScan(String[] packagesToScan) {
      this.packagesToScan = packagesToScan;
   }

   public void setEntityTypeFilters(TypeFilter[] entityTypeFilters) {
      this.entityTypeFilters = entityTypeFilters;
   }

   public void setResourceLoader(ResourceLoader resourceLoader) {
      this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
   }

   @Override
   protected void postProcessMappings(Configuration config) throws HibernateException {
      GrailsAnnotationConfiguration annConfig = (GrailsAnnotationConfiguration)config;
      if (annotatedClasses != null) {
         for (Class<?> annotatedClass : annotatedClasses) {
            annConfig.addAnnotatedClass(annotatedClass);
         }
      }
      if (annotatedPackages != null) {
         for (String annotatedPackage : annotatedPackages) {
            annConfig.addPackage(annotatedPackage);
         }
      }
      scanPackages(annConfig);
   }

   protected void scanPackages(GrailsAnnotationConfiguration config) {
      if (packagesToScan == null) {
         return;
      }

      try {
         for (String pkg : packagesToScan) {
            logger.debug('Scanning package '' + pkg + ''');
            String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                  ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN;
            Resource[] resources = resourcePatternResolver.getResources(pattern);
            MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
            for (Resource resource : resources) {
               if (resource.isReadable()) {
                  MetadataReader reader = readerFactory.getMetadataReader(resource);
                  String className = reader.getClassMetadata().getClassName();
                  if (matchesFilter(reader, readerFactory)) {
                     config.addAnnotatedClass(resourcePatternResolver.getClassLoader().loadClass(className));
                     logger.debug('Adding annotated class '' + 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);
      }
   }

   private boolean matchesFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {
      if (entityTypeFilters != null) {
         for (TypeFilter filter : entityTypeFilters) {
            if (filter.match(reader, readerFactory)) {
               return true;
            }
         }
      }
      return false;
   }
}

您可以使用与Grails注册的名称相同的名称替换应用程序的grails-app/conf/spring/resources.groovy的Grails SessionFactory bean:

import com.burtbeckwith.grails.jpa.AnnotationConfigurableLocalSessionFactoryBean

beans = {
   sessionFactory(AnnotationConfigurableLocalSessionFactoryBean) { bean ->
      bean.parent = 'abstractSessionFactoryBeanConfig'
      packagesToScan = ['com.mycompany.myapp.entity']
   }
}

在这里,我在packagesToScan属性中列出了一个程序包名称,但是您可以根据需要列出任意多个。 您还可以使用annotatedClasses属性显式列出类。 请注意,这是针对“默认”数据源的; 如果您使用多个数据源,则需要为每个数据源执行此操作。

因此,这意味着我们可以在src/groovy/com/mycompany/myapp/entity/Person.groovy定义此类:

package com.mycompany.myapp.entity

import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.Id
import javax.persistence.Version

@Entity
class Person {

   @Id @GeneratedValue
   Long id

   @Version
   @Column(nullable=false)
   Long version

   @Column(name='first', nullable=false)
   String firstName

   @Column(name='last', nullable=false)
   String lastName

   @Column(nullable=true)
   String initial

   @Column(nullable=false, unique=true, length=200)
   String email
}

它将被检测为域类,如果您运行schema-export脚本,则表DDL将位于target/ddl.sql

但是,有几个问题需要注意,主要围绕约束。 您不能在类中定义constraintsmapping块-它们将被忽略。 您将要添加的映射只需要放入批注中即可。 例如,在上面的示例中,我覆盖了firstNamelastName属性的默认名称。 但是nullable=true是JPA的默认设置,而在Grails中则相反—默认情况下,属性是必需的。 因此,尽管注释会影响数据库架构,但Grails不会使用注释中的约束,如果您无法为initial属性提供值,则将收到此类的验证错误。

您可以通过在src/java创建约束文件来解决此问题; 有关更多详细信息,请参阅文档 。 因此,在这种情况下,我将使用非静态constraints属性创建src/java/com/mycompany/myapp/entity/PersonConstraints.groovy ,例如

constraints = {
   initial(nullable: true)
   email unique: true, length: 200)
}

这样Grails约束和数据库约束是同步的。 没有这个,我将能够创建一个域类的实例,该实例具有一个包含200个以上字符的电子邮件,它将进行验证,但是在插入行时会导致数据库约束异常。

这还具有让您使用与JPA约束(例如emailblank不对应的Grails约束的好处。

参考:我们的JCG合作伙伴 Burt Beckwith在An Solipsists博客上自动发现了Grails中JPA注释的域类

翻译自: https://www.javacodegeeks.com/2012/10/grails-autodiscovery-of-jpa-annotated-domain-classes.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值