如何添加验证以限制正在创建的行数

How to add validation to limit number of rows being created

我有一个 Grails 域 class。我想添加一个验证,以便当应用程序在 TEST 环境中为 运行 时,域 class 不能有超过 100 条记录。

我想不出添加此类验证的最佳方法。我在使用 Grails 2.5.3。

这是我目前所掌握的。

class MyDomain {

String test

static constraints = {
   test blank: false, nullable: false
   id blank: false, validator: {value, command -> 
      if (Environment.current == Environment.TEST) {
          //do validation for not allowing more than 100 records
      }
   }
}

如何添加此验证?

像这样的东西对你有用吗?

class MyDomain {

String test

static constraints = {
   test blank: false, nullable: false
   id blank: false, validator: {value, command -> 
      if (Environment.current == Environment.TEST) {
          //do validation for not allowing more than 100 records
          if (MyDomain.count() > 100) return ['too.many.records']
      }
   }
}

单个域的解决方案

@Joshua 的回答非常好,但没有其他方法。其中之一是:

class MyDomain {

    String test

    void beforeInsert() {
        if (Environment.current == Environment.TEST) {
            MyDomain.withNewSession {
                if (MyDomain.count() == 100) {
                    throw new Exception("Not allowing more than 100 records")
                }
            }
        }
    }

    static constraints = {
        test blank: false
    }
}

此外,请注意两件事:

  1. id 字段上的 blank: false 没有用,因为它不是字符串,因为 blank 约束适用于字符串
  2. nullable: false 没有用,因为 nullable 约束的默认值是 false

跨域通用解决方案 TL;DR

如果您希望跨多个域执行此操作,不建议复制相同的代码,因为您的代码不会是 DRY。为此,您可以注册自定义事件侦听器:

首先在src/groovy中定义一个Java注解:

import java.lang.annotation.Documented
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target

@Documented
@Target([ElementType.TYPE, ElementType.FIELD])
@Retention(RetentionPolicy.RUNTIME)
public @interface LimitRowsFoo {

    int value() default 100
}

现在再定义一个Groovy class:

import grails.util.Environment
import org.grails.datastore.mapping.engine.event.PreInsertEvent
import org.grails.datastore.mapping.engine.event.AbstractPersistenceEvent
import org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener

class PreInsertEventListener extends AbstractPersistenceEventListener {

    PreUpdateEventListener(final Datastore datastore) {
        super(datastore)
    }

    @Override
    protected void onPersistenceEvent(AbstractPersistenceEvent event) {
        // Instance of domain which is being created
        Object domainInstance = event.entityObject

        if (Environment.current == Environment.TEST) {
            if (domainInstance.class.isAnnotationPresent(LimitRowsFoo.class)) {
                // Just using any domain reference here
                MyDomain.withNewTransaction {
                    int maxRows = domainInstance.class.getAnnotation(LimitRowsFoo.class).value()
                    if (domainInstance.class.count() == maxRows) {
                        event.cancel()
                    }
                }
            }
        }
    }

    @Override
    boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        // Only allow PreInsert event to be listened
        eventType.name == PreInsertEvent.class.name
    }
}

现在在 Bootstrap.groovy 中注册:

application.mainContext.eventTriggeringInterceptor.datastores.each { k, datastore ->
        applicationContext.addApplicationListener new PreInsertEventListener(datastore)
    }

现在,在您的域中,您所要做的就是:

@LimitRowsFoo
class MyDomain {

    String test

    static constraints = {
        test blank: false
    }
}

@LimitRowsFoo(value = 200)   // to limit records to 200
class MyAnotherDomain {

    String name
}