2

让我们假设这个构建器:

@NodeEntity
case class Meeting(@Indexed creator: String, @Indexed title: String, @Indexed description: String,
                 @Indexed activityType: String, @Indexed startSchedule: Date, @Indexed endSchedule: Date,
                 @Indexed place: String, @Indexed maxParticipants: Int) {

  @GraphId private var _id: Long = _

  private def this() {
    this("unknown", "no title", "no description", "no activity type", new Date(), new Date(), "no place", 0)
    //dummy values in order to satisfy Scala's no-arg constructor syntax (required by Spring-Data)
  }
}

object Meeting {

  def builder(): PossibleMeeting = PossibleMeeting()

  case class PossibleMeeting(creator: String = "", title: String = "", description: String = "",
                           activityType: String = "", schedule: (Date, Date) = (new Date(), new Date()),
                           place: String = "", maxParticipants: Int = 0) {

    def withCreator(creator: String) = {
        this.copy(creator = creator)
      }

      def withTitle(title: String) = {
        this.copy(title = title)
      }

      def withDescription(description: String) = {
        this.copy(description = description)
      }

      def withActivityType(activityType: String) = {
        this.copy(activityType = activityType)
      }

      def withSchedule(schedule: (Date, Date)) = {
        this.copy(schedule = schedule)
      }

      def withPlace(place: String) = {
        this.copy(place = place)
      }

      def withMaxParticipants(maxParticipants: Int) = {
        this.copy(maxParticipants = maxParticipants)
      }

      def build(validator: PossibleMeetingValidator) = {
        this.toMeeting(validator)
      }

    def toMeeting(possibleMeetingValidator: PossibleMeetingValidator): ValidationNEL[MeetingValidationError, Meeting] =
      (possibleMeetingValidator.checkForNonEmpty("creator", creator, "Creator must not be empty") |@|
        possibleMeetingValidator.checkForNonEmpty("title", title, "Title must not be empty") |@|
        possibleMeetingValidator.checkForNonEmpty("description", description, "Description must not be empty") |@|
        possibleMeetingValidator.checkForNonEmpty("activityType", activityType, "Activity type must not be empty") |@|
        possibleMeetingValidator.checkForEndScheduleConsistency("endSchedule", schedule._1, schedule._2, "Schedule is not consistent since endSchedule date is anterior to startSchedule date") |@|
        possibleMeetingValidator.checkForNonEmpty("place", place, "Place must not be empty"))(Meeting(_, _, _, _, schedule._1, _, _, maxParticipants))

  }

Spring-Data-Neo4j@NodeEntity使用注释将对象保存为图形节点。

我想强制客户端使用此调用语法来创建Meeting

Meeting.builder().
          withCreator(creator).
          withTitle(title).withDescription(description).
          withActivityType(activityType).
          withSchedule(schedule).withPlace(place).
          withMaxParticipants(maxParticipants).build(PossibleMeetingValidator())

而不是冒险的直接方式:

Meeting(creator, title, description, activityType, schedule, place, maxParticipants)

我尝试了扩展 a 的著名技巧case class,在这里Meeting通过一个密封的特征并将其设为Meeting私有。此链接显示了一个示例:隐藏案例类允许泄漏

但是,使用此解决方案,Spring-Data会引发错误,通知Meeting必须具有公共构造函数(即公共类/案例类)。

是否有另一种隐藏案例类以防止外部直接使用的方法,以便 Spring-Data 不会抱怨访问?这似乎很困难,如果不是不可能的话,但是......它会很棒:)

4

1 回答 1

2

好吧,如果我理解正确,这归结为有一个 spring 可以调用的构造函数,但 scala 代码不能。我看到的一个(丑陋的)解决方案是使 cosntructor 对Meeting单例私有。这将防止 scala 代码(除了单例)直接实例化它。另一方面,由于java不支持“作用域私有”的概念,所以从java代码的角度来看,构造函数实际上是public的。特别是当通过java反射访问构造函数时,构造函数被视为公共的,可以被调用。这意味着 spring 实例化应该没有问题Meeting,但同时 scala 代码将无法直接实例化它。正是你想要的。

所以你所要做的就是这个(注意private参数列表之前的关键字):

@NodeEntity
case class Meeting private[Meeting] (@Indexed creator: String, @Indexed title: String, @Indexed description: String,
             @Indexed activityType: String, @Indexed startSchedule: Date, @Indexed endSchedule: Date,
             @Indexed place: String, @Indexed maxParticipants: Int) {
 ...

This should work as expected, though it's a bit gross to rely on the imperfect mapping of scala code to the JVM model.

Another cleaner alternative would be to teach spring how to instantitate your class, using a custom factory that calls Metting.builder. I won't comment further on this solution as I have no experience with spring. However from a quick glance it seems that the following is relevant: http://static.springsource.org/spring/docs/2.0.x/reference/beans.html#beans-factory-extension-factorybean

于 2013-02-22T12:08:05.870 回答