我正在学习Play Framework,我需要从网络表单中将Address
绑定到Person
。
有我的实体:
@Entity
@Table(name = "address")
public class Address {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String country;
@OneToMany(mappedBy = "address", fetch = FetchType.EAGER)
private List<Person> persons;
// get/set
}
@Entity
@Table(name = "person")
public class Person {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String name;
@ManyToOne(fetch = FetchType.EAGER)
private Address address;
// get/set
}
我有以下控制器和dao:
public class HomeController extends Controller {
@Inject
private Configuration configuration;
@Inject
private PersonManager personManager;
@Inject
private AddressManager addressManager;
@Inject
FormFactory formFactory;
@Inject
Formatters formatters;
@Transactional
public Result index() {
List<Person> persons = personManager.findAll();
List<Address> addresses = addressManager.findAll();
return ok(index.render("Hello, world", persons, addresses));
}
@Transactional
public Result addPerson() {
DynamicForm requestData = formFactory.form().bindFromRequest();
System.out.println("form: " + requestData.get("address"));
Person person = Form.form(Person.class).bindFromRequest().get();
personManager.save(person);
return redirect(routes.HomeController.index());
}
@Transactional
public Result addAddress() {
Address address = Form.form(Address.class).bindFromRequest().get();
addressManager.save(address);
return redirect(routes.HomeController.index());
}
// ...
}
package dao;
public class PersonDaoImpl implements PersonDao {
@Inject
private JPAApi jpaApi;
@Override
public void save(Person user) {
jpaApi.em().persist(user);
}
@Override
public void delete(Person user) {
jpaApi.em().remove(user);
}
@Override
public void update(Person user) {
jpaApi.em().merge(user);
}
@Override
public List<Person> findAll() {
return jpaApi.em().createQuery("from Person").getResultList();
}
@Override
public Person findById(int id) {
return (Person) jpaApi.em().find(Person.class, id);
}
@Override
public Person findByName(String name) {
return (Person) jpaApi.em().createQuery("SELECT p FROM Person p WHERE p.name LIKE :name").setParameter("name", name).getSingleResult();
}
}
package dao;
public class AddreessDaoImpl implements AddressDao {
@Inject
private JPAApi jpaApi;
@Override
public void save(Address address) {
jpaApi.em().persist(address);
}
@Override
public void delete(Address address) {
jpaApi.em().remove(address);
}
@Override
public void update(Address address) {
jpaApi.em().merge(address);
}
@Override
public List<Address> findAll() {
return jpaApi.em().createQuery("from Address").getResultList();
}
@Override
public Address findById(int id) {
return (Address) jpaApi.em().find(Address.class, id);
}
@Override
public Address findByCountry(String name) {
return (Address) jpaApi.em().createQuery("SELECT a FROM Address a WHERE a.country LIKE :country").setParameter("country", name).getSingleResult();
}
}
查看:
@(message: String, persons: List[Person], addresses: List[Address])
@main("Welcome to Play") {
<form action="@routes.HomeController.addPerson()" method="post">
<input type="text" name="name" />
<select name="address">
@for(address <- addresses) {
<option value="@address.getId">
@address.getCountry
</option>
}
</select>
<button>Add Person</button>
</form>
}
我需要添加具有现有地址的新人。我正在尝试使用Formatters
package service;
import dao.AddressManager;
import models.Address;
import play.data.format.Formatters;
import play.i18n.MessagesApi;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import java.text.ParseException;
import java.util.Locale;
@Singleton
public class FormattersProvider implements Provider<Formatters> {
private final MessagesApi messagesApi;
@Inject
public FormattersProvider(MessagesApi messagesApi) {
this.messagesApi = messagesApi;
}
@Inject
private AddressManager addressManager;
@Override
public Formatters get() {
Formatters formatters = new Formatters(messagesApi);
formatters.register(Address.class, new Formatters.SimpleFormatter<Address>(){
@Override
public Address parse(String input, Locale locale) throws ParseException {
Address address = addressManager.findById(Integer.getInteger(input));
return address;
}
@Override
public String print(Address address, Locale locale) {
return address.getCountry();
}
});
return formatters;
}
}
package service;
import com.google.inject.AbstractModule;
import play.data.format.Formatters;
public class FormattersModule extends AbstractModule {
@Override
protected void configure() {
bind(Formatters.class).toProvider(FormattersProvider.class);
}
}
但我怎么能使用Formatters?
如果我添加地址,那就没关系。
但是如果我使用db中的web表单地址列添加新的Person有NULL
值,或者app崩溃:
[error] application -
! @720fgnbfl - Internal server error, for (POST) [/person] ->
play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[CompletionException: java.lang.IllegalStateException: Error(s) binding form: {"address":["Invalid value"]}]]
at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:280)
at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:206)
at play.api.GlobalSettings$class.onError(GlobalSettings.scala:160)
at play.api.DefaultGlobal$.onError(GlobalSettings.scala:188)
at play.api.http.GlobalSettingsHttpErrorHandler.onServerError(HttpErrorHandler.scala:98)
at play.core.server.netty.PlayRequestHandler$$anonfun$2$$anonfun$apply$1.applyOrElse(PlayRequestHandler.scala:100)
at play.core.server.netty.PlayRequestHandler$$anonfun$2$$anonfun$apply$1.applyOrElse(PlayRequestHandler.scala:99)
at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:344)
at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:343)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
Caused by: java.util.concurrent.CompletionException: java.lang.IllegalStateException: Error(s) binding form: {"address":["Invalid value"]}
at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:292)
at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:308)
at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:593)
at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:577)
at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:474)
at java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:1977)
at scala.concurrent.java8.FuturesConvertersImpl$CF.apply(FutureConvertersImpl.scala:21)
at scala.concurrent.java8.FuturesConvertersImpl$CF.apply(FutureConvertersImpl.scala:18)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
at scala.concurrent.BatchingExecutor$Batch$$anonfun$run$1.processBatch$1(BatchingExecutor.scala:63)
Caused by: java.lang.IllegalStateException: Error(s) binding form: {"address":["Invalid value"]}
at play.data.Form.get(Form.java:634)
at controllers.HomeController.addPerson(HomeController.java:106)
at router.Routes$$anonfun$routes$1$$anonfun$applyOrElse$2$$anonfun$apply$2.apply(Routes.scala:293)
at router.Routes$$anonfun$routes$1$$anonfun$applyOrElse$2$$anonfun$apply$2.apply(Routes.scala:293)
at play.core.routing.HandlerInvokerFactory$$anon$4.resultCall(HandlerInvoker.scala:157)
at play.core.routing.HandlerInvokerFactory$$anon$4.resultCall(HandlerInvoker.scala:156)
at play.core.routing.HandlerInvokerFactory$JavaActionInvokerFactory$$anon$14$$anon$3$$anon$1.invocation(HandlerInvoker.scala:136)
at play.core.j.JavaAction$$anon$1.call(JavaAction.scala:73)
at play.http.HttpRequestHandler$1.call(HttpRequestHandler.java:54)
at play.db.jpa.TransactionalAction.lambda$call$4(TransactionalAction.java:28)
为什么呢?我怎么绑它?