2

我想要实现的只是这个示例应用程序:~\play-2.1.0\samples\java\forms

更新的最新代码:

我的question.scala.html样子:

@(questionForm: Form[Question])

@import helper._
@import helper.twitterBootstrap._



@answerField(field: Field, className: String = "answer") = {

<div class="twipsies well @className">
    <table>
        <tr>
            <td> @checkbox(field("addRight"),'_label -> "Add")</td>
            <td> @checkbox(field("editRight"),'_label -> "Edit")</td>
            <td> @checkbox(field("delRight"),'_label -> "Delete")</td>
            <td> @checkbox(field("viewRight"),'_label -> "View")</td>
        </tr>
    </table>
</div>

}


@if(questionForm.hasErrors) {
    <div class="alert-message error">
        <p><strong>Oops</strong> Please fix all errors</p>
    </div>
}

@helper.form(action = routes.Questions.submit, 'id -> "form") {

    <fieldset>

        @inputText(
            questionForm("name"),
            '_label -> "Name of a Right"
        )

        @inputText(
            questionForm("namex"),
            '_label -> "Name of a Right"
        )

        <div class="answers">

            @repeat(questionForm("answers"), min = 0) { answer =>
                @answerField(answer)
            }



            @**
            * Keep an hidden block that will be used as template for Javascript copy code
            * answer_template is only css style to make it hidden (look main.css and declare your own answer_template at bottom)
            **@
            @answerField(
                questionForm("answers"),
                className = "answer_template"
            )

            <div class="clearfix">
                <div class="input">
                    <a class="addAnswer btn success">Add </a>
                </div>
            </div>

        </div>
    </fieldset>

    <div class="actions">
        <input type="submit" class="btn primary" value="Insert">
    </div>

}

<script type="text/javascript" charset="utf-8">

        $('.removeAnswer').on('click', function(e) {
        var answers = $(this).parents('.answers');
        $(this).parents('.answer').remove();
        renumber(answers);
        });

        $('.addAnswer').on('click', function(e) {
        var answers = $(this).parents('.answers');
        var template = $('.answer_template', answers);
        template.before('<div class="clearfix answer">' + template.html() + '</div>');
        renumber(answers);
        });

        $('#form').submit(function() {
        $('.answer_template').remove()
        });

        // -- renumber fields
        // This is probably not the easiest way to do it. A jQuery plugin would help.

        var renumber = function(answers) {
        $('.answer').each(function(i) {
        $('input', this).each(function() {
        $(this).attr('name', $(this).attr('name').replace(/answers\[.+?\]/g, 'answers[' + i + ']'))
        });
        });
        }

</script>

……

问题模型:

package models;

import play.data.validation.Constraints;
import play.db.ebean.Model;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;


@Entity
public class Question extends Model {

@Id
public Long id;

@Constraints.Required
public String name;


@OneToMany(cascade = CascadeType.PERSIST, mappedBy = "question")
public List<Answer> answers;

public Question() {

}

public Question(String name) {
    this.name = name;

}
}

答案型号:

@Entity
public  class Answer extends Model {

@Id
public Long id;

public boolean addRight;
public boolean editRight;
public boolean delRight;
public boolean viewRight;

@ManyToOne
public Question question;

public Answer() {
}

public Answer(boolean addRight,boolean editRight,boolean delRight,boolean viewRight, 
Question question) {
    this.addRight = addRight;
    this.editRight = editRight;
    this.delRight = delRight;
    this.viewRight = viewRight;
    this.question = question;
}
}

最后控制器保存部分:

  public static Result submit() {
    Form<Question> filledForm = questionForm.bindFromRequest();

    if(filledForm.hasErrors()) {
        User user = User.findByUserName("samuel");
        return badRequest(question.render(filledForm));
    }
    else {
        // If we dont have any errors, we should be around here :)
        Question question = filledForm.get();
        // Since Answer needs reference to Question and with new Question
        // it cant get it loaded from DB we need to do little dirty trick here
        // in order to save new question id instantly to answers foreign_key
        // as question_id, otherwise it will be null
        System.out.println("-->" + question.answers.size() );
        if(question.answers != null) {
            ArrayList<Answer> answersCopy = new ArrayList<Answer>();
            Logger.trace("Size of Anwsers : " +answersCopy.size());
            for(Answer answer : question.answers) {
                answersCopy.add(new                  
   Answer(answer.addRight,answer.editRight,answer.delRight,answer.viewRight,question));
                System.out.println("##" + answer.addRight);
            }
            question.answers = answersCopy;
        }
        question.save();
        return ok("Nice, all saved!");

    }
}

使用上面的代码,我没有得到任何异常但是.. 问题部分保存了 Anwser。

谢谢

4

1 回答 1

2

好吧,我想我用 JPA 让它工作了,我有点不确定你是想用 JPA 还是只用 Ebean,但是我认为你和其他人也可以通过 Ebean 移植它。(如果你需要,我也可以做到,我猜;)

这是我用 JPA 尝试过的东西:

  • 打开终端(或 cmd)并导航到名为的项目play-2.1.0/samples/java/forms
  • 然后(以防万一)运行这些命令:play clean .. play compile .. play eclipse
  • 去 eclipse 并在那里导入它(右键单击包资源管理器 -> 现有项目..)
  • 打开conf/application.conf并添加(或取消注释)这些行:
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
db.default.jndiName=DefaultDS
jpa.default=defaultPersistenceUnit
  • 创建文件夹结构conf/evolutions/defaultconf/META-INF
  • 添加persistence.xml具有以下内容的文件:

http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">

<persistence-unit name="defaultPersistenceUnit" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <non-jta-data-source>DefaultDS</non-jta-data-source>
    <properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
    </properties>
</persistence-unit>
 </persistence>
  • 现在让我们为您的 Answer 和 Question 模型创建表格,创建1.sql包含evolutions/default以下内容的文件:
# --- !Ups

create table question (
  id                        bigint not null,
  name                      varchar(255),
  constraint pk_question primary key (id))
;

create table answer (
  id                        bigint not null,
  name                      varchar(255),
  question_id               bigint,
  constraint pk_answer primary key (id))
;

create sequence answer_seq start with 1000;
create sequence question_seq start with 1000;

alter table answer add constraint fk_answer_question_1 foreign key (question_id) references question (id) on delete restrict on update restrict;

# --- !Downs

SET REFERENTIAL_INTEGRITY FALSE;

drop table if exists question;
drop table if exists answer;

SET REFERENTIAL_INTEGRITY TRUE;

drop sequence if exists question_seq;
drop sequence if exists answer_seq;
  • 修改或添加以下模型:
package models;

import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.ManyToOne; 

import play.data.validation.Constraints.Required;

@Entity 
@SequenceGenerator(name = "answer_seq", sequenceName = "answer_seq")
public class Answer {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "answer_seq")
public Long id;

@Required
public String name;

@ManyToOne
public Question question;

public Answer() { 
}

public Answer(String name, Question question) {   
      this.name = name;
      this.question = question;
}

}

...

package models;

import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import play.data.validation.Constraints.Required;
import play.db.jpa.*;

@Entity
@SequenceGenerator(name = "question_seq", sequenceName = "question_seq")
public class Question {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "question_seq")
public Long id;

@Required
public String name;

@Valid
@OneToMany(cascade = CascadeType.PERSIST, mappedBy = "question")
public List<Answer> answers;

public Question() {

}

public Question(String name) {
      this.name = name;
}

public void save() {

      JPA.em().persist(this);

      List<Question> allQuestions = JPA.em().createQuery("from Question order by name").getResultList();

      System.out.println("Number of questions: " + allQuestions.size());

      for(Question q : allQuestions) {

             System.out.println("Question --- id: " + q.id + ", name: " + q.name);

                if(q.answers != null) {

                   for(Answer a : q.answers) {
                           System.out.println("Answer --- id: " + a.id + ", name: " + a.name + " question_id: " + a.question.id);
                   }
             }
      }

}

}
  • 好的,现在模型 OK 让我们创建静止的控制器和视图来测试它,从routes文件开始:
# Question
GET     /questions                  controllers.Questions.blank()
POST    /questions                  controllers.Questions.submit()
  • 然后添加以下控制器:
package controllers;

import static play.data.Form.form;
import java.util.ArrayList;
import models.Answer;
import models.Question;
import play.data.Form;
import play.db.jpa.Transactional;
import play.mvc.Controller;
import play.mvc.Result;
import views.html.question.*;

public class Questions extends Controller {

/**
* Defines a form wrapping the Question class.
*/ 
final static Form<Question> questionForm = form(Question.class);

/**
* Display a blank form.
*/ 
public static Result blank() {
    return ok(form.render(questionForm));
}

@Transactional
public static Result submit() {

Form<Question> filledForm = questionForm.bindFromRequest();

if(filledForm.hasErrors()) {
     return badRequest(form.render(filledForm));
} else {

// If we dont have any errors, we should be around here :)
Question question = filledForm.get();

// Since Answer needs reference to Question and with new Question
// it cant get it loaded from DB we need to do little dirty trick here
// in order to save new question id instantly to answers foreign_key
// as question_id, otherwise it will be null
if(question.answers != null) {
     ArrayList<Answer> answersCopy = new ArrayList<Answer>();
     for(Answer answer : question.answers) {
     answersCopy.add(new Answer(answer.name, question));
}
question.answers = answersCopy;
}
question.save();

// You can also use this test code to save data
// Question question = new Question("What is your favorite color?");
// question.answers = new ArrayList<Answer>();
// question.answers.add(new Answer("Blue", question));
// question.answers.add(new Answer("Red", question));
// question.save();

return ok("Nice, all saved!");

}
}

}
  • 好的,还有一个视图,将其views.question命名为form.scala.html(顺便说一句,我重用了应用程序已经用于联系人的类似代码,但使其与您的模型一起使用。)
@(questionForm: Form[Question])

@import helper._ 
@import helper.twitterBootstrap._

@title = {
     Add a new question 
}

@answerField(field: Field, className: String = "answer") = {
    @input(field, '_label -> "Answer", '_class -> className) { (id, name, value, _) =>
    <input type="text" name="@name" value="@value"> 
    <a class="removeAnswer btn danger">Remove</a>
} 
}

@main(title, nav = "question") {

@if(questionForm.hasErrors) {
    <div class="alert-message error">
        <p><strong>Oops</strong> Please fix all errors</p>
    </div>
}

@helper.form(action = routes.Questions.submit, 'id -> "form") {

<fieldset>
<legend>Fill a question with answers</legend>

@inputText(
    questionForm("name"), 
    '_label -> "Name of a question"
)

<div class="answers">

@repeat(questionForm("answers"), min = 0) { answer =>
     @answerField(answer("name"))
}

@**
* Keep an hidden block that will be used as template for Javascript copy code
* answer_template is only css style to make it hidden (look main.css and declare your own answer_template at bottom)
**@
@answerField(
    questionForm("answers[x].name"),
    className = "answer_template"
)

<div class="clearfix">
<div class="input">
    <a class="addAnswer btn success">Add answer</a>
</div>
</div>

</div>
</fieldset>

<div class="actions">
<input type="submit" class="btn primary" value="Insert">
<a href="@routes.Application.index" class="btn">Cancel</a>
</div>

}

<script type="text/javascript" charset="utf-8">

$('.removeAnswer').live('click', function(e) {
    var answers = $(this).parents('.answers');
    $(this).parents('.answer').remove();
    renumber(answers);
});

$('.addAnswer').live('click', function(e) {
    var answers = $(this).parents('.answers');
    var template = $('.answer_template', answers);
    template.before('<div class="clearfix answer">' + template.html() + '</div>');
    renumber(answers);
});

$('#form').submit(function() {
    $('.answer_template').remove()
});

// -- renumber fields
// This is probably not the easiest way to do it. A jQuery plugin would help.

var renumber = function(answers) {
$('.answer').each(function(i) {
    $('input', this).each(function() {
         $(this).attr('name', $(this).attr('name').replace(/answers\[.+?\]/g, 'answers[' + i + ']'))
         });
    });
}

</script>
}
  • 作为最终调整,检查 main.css 文件的最后一行,您记得将其编辑为:
.phone_template, .profile_template, .answer_template {
     display: none;
}
  • 并添加main.scala.html
<li class="@("active".when(nav == "question"))">
   <a href="@routes.Questions.blank()">Questions</a>
</li>
  • 现在,如果您还没有这样做,请转到您的浏览器,确认要运行的演进并导航到问题视图并尝试使用不同的问题和值提交您的表单。

您应该在终端播放窗口中看到结果(类似于下面的我的),如果是这种情况,您刚刚成功保存了几乎没有答案的新问题,您可以随意重复它 - 应该可以工作,因为总是会生成新的 id . 如果您希望您的表单还包含编辑等,我建议检查 JPA 在播放示例文件夹 (computer-database-jpa) 中的使用方式,因为这个示例已经变得太大了;稍后我可能会把它扔到 github,晚安,干杯。

Number of questions: 1
Question --- id: 50000, name: What is your favorite color?
Answer --- id: 50000, name: Blue question_id: 50000
Answer --- id: 50001, name: Red question_id: 50000
于 2013-03-11T19:52:31.840 回答