Playframework 对象的部分更新会丢失OneToMany关系

Playframework 对象的部分更新会丢失OneToMany关系,playframework,playframework-2.0,Playframework,Playframework 2.0,我在更新对象时遇到问题。 我的对象具有这些属性(以及更多属性,但问题是相同的) 这是表格上的 User [username=null, testString=null, jobs=[], mails=[], getCreated()=null, getModified()=null, getId()=1] 这就是更新后的对象 User [username=Timmeey, testString=null, jobs=[], mails=[], getCreated()=140164753762

我在更新对象时遇到问题。 我的对象具有这些属性(以及更多属性,但问题是相同的)

这是表格上的

User [username=null, testString=null, jobs=[], mails=[], getCreated()=null, getModified()=null, getId()=1]
这就是更新后的对象

User [username=Timmeey, testString=null, jobs=[], mails=[], getCreated()=1401647537627, getModified()=1401647537727, getId()=1]
正如我们所看到的,play并没有覆盖所有的空字段,因为用户名仍然存在,只是邮件字段被覆盖了

当然,我知道我可以手动操作并迭代fieleds,检查它们是否为null,然后设置应该自己更新的字段,但我无法想象这应该是一种方式

多谢各位

TL;博士 Play/Ebean在更新已保存的对象时识别空值。因此,它将执行部分更新,仅覆盖具有非空值的字段。这适用于简单的事情,比如字符串用户名。但是当涉及到像@OneToMany这样的关系时,它失败了,并且总是用新对象的值覆盖存储的值,即使它是空的。 我想要的是,关系字段也被视为普通字段,当字段为null时,旧对象上的字段不应被覆盖

整个模型班

package models;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

import javax.naming.directory.InvalidAttributeValueException;
import javax.persistence.CascadeType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.Valid;
import javax.validation.constraints.Null;

import com.avaje.ebean.Ebean;

import controllers.SettingsController;
import exceptions.InputValidationException;
import exceptions.NotYetInitializedException;
import play.Logger;
import play.data.validation.ValidationError;

@Entity
@Table(name = "userTable")
@DiscriminatorValue("aUser")
// User may be a reserved keyword in some sql databases
public class User extends AbstractSuperModel {
final static Logger.ALogger logger = Logger.of(User.class);
final private static String usernameRegexPattern = "[\\w_-]{3,}";

// This prevents binding of this value from forms
@Null
@javax.persistence.Column(unique = true)
private String username; // Must not be set by forms, only by controllers

// @OneToMany
// private Set<Ticket> responsibleForTickets;
// @ManyToMany
// private Set<Ticket> subscribedTickets;
// @OneToMany
// private Set<Ticket> reportedTickets;
// //public String secondaryEmails;

private String testString;

@ManyToMany(cascade = CascadeType.PERSIST)
private List<Job> jobs;

@Null
@OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
public List<Mail> mails;

public User(String username) throws InputValidationException {
    logger.info("Adding a new User: " + username);
    Pattern regex = Pattern.compile(usernameRegexPattern);
    if (!regex.matcher(username).matches()) {
        logger.error(username
                + " is not a valid username. In case you not just tried to troll the system, consider this as a serious Error and contact the maintainer (Timmeey@xxx.xxx (2014)");
        throw new InputValidationException(username
                + " is not a valid username");

    }

    this.setUsername(username);
    Mail mail = new Mail();
    mail.setMailAddr(username + "@xxx.xxx");
    mail.setIsMainMail(true);
    this.addMail(mail);

}

public void addMail(Mail mail) {
    this.getMails().add(mail);
}

public Mail getMainMail() {
    for (Mail mail : this.getMails()) {
        if (mail != null && mail.getIsMainMail()) {
            return mail;
        }
    }
    return null;
}

/**
 * Removes a Mail from the User.
 * 
 * @param mail
 *            The mail that should get removed
 */
public void removeMail(Mail mail) {
    for (Iterator<Mail> iterator = this.getMails().iterator(); iterator
            .hasNext();) {
        Mail tmpMail = iterator.next();
        if (tmpMail.getMailAddr().equalsIgnoreCase(mail.getMailAddr())) {
            tmpMail.delete();
            return;
        }

    }
}

public void addJob(Job job) {
    this.jobs.add(job);
    this.update();
}

public void removeJob(Job job) {
    this.getJobs().remove(job);
    this.update();
}

public static Finder<Long, User> find = new Finder<Long, User>(Long.class,
        User.class);



public static List<User> findAll() {
    return User.find.all();
}

public static User findById(final Long id) {
    return User.find.byId(id);
}

public static User findByMail(final String address) {
    final Mail mail = Mail.findByAddr(address);
    if (mail != null) {
        return mail.getUser();
    }
    return null;
}

public static User findByName(final String username) {
    User resultUser = null;
    resultUser = User.find.where().eq("username", username).findUnique();
    return resultUser;
}

public static boolean isKnownUser(final String username) {
    return findByName(username) != null;
}

/**
 * Will be executed before a User is saved into the Database.
 */
@PreUpdate
public void processMailAddresses() {
}

public List<ValidationError> validate() {
    List<ValidationError> errors = new ArrayList<ValidationError>();
    if (errors.size() != 0) {
        return errors;
    }
    return null;
}

public List<Job> getJobs() {
    return this.jobs;
}

public List<Mail> getMails() {
    if (this.mails == null) {
        return null;
    }
    return this.mails;
}

public String getTestString() {
    return this.testString;
}

public String getUsername() {
    return this.username;
}

public void setJobs(final List<Job> jobs) {
    this.jobs = jobs;
}

public void setMails(final List<Mail> mails) {
    this.mails = mails;
}

public void setTestString(final String testString) {
    this.testString = testString;
}

public void setUsername(final String username) {
    this.username = username;
}

public boolean hasJob(Job job) {
    if (getJobs() == null) {
        return false;
    }
    return this.getJobs().contains(job);
}

@Override
public String toString() {
    final List<String> jobs = new ArrayList<String>();
    for (Job job : this.getJobs()) {
        jobs.add(job.getJobName());
    }

    final List<String> mails = new ArrayList<String>();
    for (final Mail mail : this.getMails()) {
        mails.add(mail.getMailAddr() + ", isMainMail: "
                + mail.getIsMainMail() + ", id: " + mail.getId());
    }

    return String
            .format("User [username=%s, testString=%s, jobs=%s, mails=%s, getCreated()=%s, getModified()=%s, getId()=%s]",
                    this.username, this.testString, jobs, mails,
                    this.getCreated(), this.getModified(), this.getId());
}


}
封装模型;
导入java.util.ArrayList;
导入java.util.Iterator;
导入java.util.List;
导入java.util.regex.Pattern;
导入javax.naming.directory.InvalidateTributeValueException;
导入javax.persistence.CascadeType;
导入javax.persistence.DiscriminatorValue;
导入javax.persistence.Entity;
导入javax.persistence.ManyToMany;
导入javax.persistence.OneToMany;
导入javax.persistence.OneToOne;
导入javax.persistence.PrePersist;
导入javax.persistence.PreUpdate;
导入javax.persistence.Table;
导入javax.persistence.Transient;
导入javax.validation.Valid;
导入javax.validation.constraints.Null;
进口com.avaje.ebean.ebean;
导入控制器。设置控制器;
导入异常。InputValidationException;
导入异常。NotYetInitializedException;
导入play.Logger;
导入play.data.validation.ValidationError;
@实体
@表(name=“userTable”)
@鉴别器值(“aUser”)
//用户可能是某些sql数据库中的保留关键字
公共类用户扩展抽象超级模型{
最终静态记录器.ALogger记录器=记录器.of(用户类);
最后一个私有静态字符串usernameregexpatern=“[\\w \-]{3,}”;
//这将防止从表单绑定此值
@空的
@javax.persistence.Column(unique=true)
私有字符串username;//不能由窗体设置,只能由控制器设置
//@OneToMany
//私人设置责任档案;
//@manytomy
//私人订票;
//@OneToMany
//私有集合报告的tickets;
////公共字符串二次电子邮件;
私有字符串testString;
@ManyToMany(cascade=CascadeType.PERSIST)
私人名单工作;
@空的
@OneToMany(cascade=CascadeType.ALL,mappedBy=“user”)
公开名单邮件;
公共用户(字符串用户名)抛出InputValidationException{
logger.info(“添加新用户:“+用户名”);
Pattern regex=Pattern.compile(usernameregexpatern);
如果(!regex.matcher(username.matches()){
logger.error(用户名
“不是”一个有效的用户名。如果你不是试图操纵系统,就认为这是一个严重的错误,并联系维护者。Timmeey@xxx.xxx (2014)");
抛出新的InputValidationException(用户名
+“不是有效的用户名”);
}
这个.setUsername(username);
邮件=新邮件();
mail.setMailAddr(用户名+“@xxx.xxx”);
mail.setIsMainMail(true);
这个.addMail(mail);
}
公共无效添加邮件(邮件){
this.getMail().add(邮件);
}
公共邮件getMainMail(){
for(邮件:this.getMail()){
if(mail!=null&&mail.getIsMainMail(){
回信;
}
}
返回null;
}
/**
*从用户中删除邮件。
* 
*@param-mail
*应该删除的邮件
*/
公共无效删除邮件(邮件){
for(Iterator Iterator=this.getMail().Iterator();Iterator
.hasNext();){
Mail tmpMail=iterator.next();
if(tmpMail.getMailAddr().equalsIgnoreCase(mail.getMailAddr())){
tmpMail.delete();
返回;
}
}
}
公共无效添加作业(作业作业){
this.jobs.add(job);
这个.update();
}
公共作废移除作业(作业作业){
this.getJobs().remove(作业);
这个.update();
}
公共静态查找器find=新查找器(Long.class,
用户类别);
公共静态列表findAll(){
返回User.find.all();
}
公共静态用户findById(最终长id){
返回User.find.byId(id);
}
公共静态用户findByMail(最终字符串地址){
最终邮件=Mail.findByAddr(地址);
如果(邮件!=null){
return mail.getUser();
}
返回null;
}
公共静态用户findByName(最终字符串用户名){
用户resultUser=null;
resultUser=User.find.where().eq(“用户名”,username.findUnique();
返回结果器;
}
公共静态布尔值isKnownUser(最终字符串用户名){
返回findByName(用户名)!=null;
}
/**
*将在用户保存到数据库之前执行。
*/
@预更新
public void processMailAddresses(){
}
公共列表验证(){
列表错误=新建ArrayList();
如果(errors.size()!=0){
返回错误;
}
返回null;
}
公共列表getJobs(){
把这个还给我;
}
公共列表getMail(){
if(this.mails==null){
返回null;
}
将此邮件退回;
}
公共字符串getTestString(){
返回this.testString;
}
公共字符串getUsername(){
返回此用户名;
}
公共无效设置作业(最终列表作业){
这个.工作=工作;
}
公开作废邮件(最终列表邮件){
this.mails=邮件;
}
公共void setTestString(最终字符串testString){
this.testString=testString;
}
public void setUsername(最终字符串用户名){
this.username=用户名;
}
公共作业(作业作业){
如果(getJobs()==null){
返回false;
}
雷图
User [username=null, testString=null, jobs=[], mails=[], getCreated()=null, getModified()=null, getId()=1]
User [username=Timmeey, testString=null, jobs=[], mails=[], getCreated()=1401647537627, getModified()=1401647537727, getId()=1]
package models;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

import javax.naming.directory.InvalidAttributeValueException;
import javax.persistence.CascadeType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.Valid;
import javax.validation.constraints.Null;

import com.avaje.ebean.Ebean;

import controllers.SettingsController;
import exceptions.InputValidationException;
import exceptions.NotYetInitializedException;
import play.Logger;
import play.data.validation.ValidationError;

@Entity
@Table(name = "userTable")
@DiscriminatorValue("aUser")
// User may be a reserved keyword in some sql databases
public class User extends AbstractSuperModel {
final static Logger.ALogger logger = Logger.of(User.class);
final private static String usernameRegexPattern = "[\\w_-]{3,}";

// This prevents binding of this value from forms
@Null
@javax.persistence.Column(unique = true)
private String username; // Must not be set by forms, only by controllers

// @OneToMany
// private Set<Ticket> responsibleForTickets;
// @ManyToMany
// private Set<Ticket> subscribedTickets;
// @OneToMany
// private Set<Ticket> reportedTickets;
// //public String secondaryEmails;

private String testString;

@ManyToMany(cascade = CascadeType.PERSIST)
private List<Job> jobs;

@Null
@OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
public List<Mail> mails;

public User(String username) throws InputValidationException {
    logger.info("Adding a new User: " + username);
    Pattern regex = Pattern.compile(usernameRegexPattern);
    if (!regex.matcher(username).matches()) {
        logger.error(username
                + " is not a valid username. In case you not just tried to troll the system, consider this as a serious Error and contact the maintainer (Timmeey@xxx.xxx (2014)");
        throw new InputValidationException(username
                + " is not a valid username");

    }

    this.setUsername(username);
    Mail mail = new Mail();
    mail.setMailAddr(username + "@xxx.xxx");
    mail.setIsMainMail(true);
    this.addMail(mail);

}

public void addMail(Mail mail) {
    this.getMails().add(mail);
}

public Mail getMainMail() {
    for (Mail mail : this.getMails()) {
        if (mail != null && mail.getIsMainMail()) {
            return mail;
        }
    }
    return null;
}

/**
 * Removes a Mail from the User.
 * 
 * @param mail
 *            The mail that should get removed
 */
public void removeMail(Mail mail) {
    for (Iterator<Mail> iterator = this.getMails().iterator(); iterator
            .hasNext();) {
        Mail tmpMail = iterator.next();
        if (tmpMail.getMailAddr().equalsIgnoreCase(mail.getMailAddr())) {
            tmpMail.delete();
            return;
        }

    }
}

public void addJob(Job job) {
    this.jobs.add(job);
    this.update();
}

public void removeJob(Job job) {
    this.getJobs().remove(job);
    this.update();
}

public static Finder<Long, User> find = new Finder<Long, User>(Long.class,
        User.class);



public static List<User> findAll() {
    return User.find.all();
}

public static User findById(final Long id) {
    return User.find.byId(id);
}

public static User findByMail(final String address) {
    final Mail mail = Mail.findByAddr(address);
    if (mail != null) {
        return mail.getUser();
    }
    return null;
}

public static User findByName(final String username) {
    User resultUser = null;
    resultUser = User.find.where().eq("username", username).findUnique();
    return resultUser;
}

public static boolean isKnownUser(final String username) {
    return findByName(username) != null;
}

/**
 * Will be executed before a User is saved into the Database.
 */
@PreUpdate
public void processMailAddresses() {
}

public List<ValidationError> validate() {
    List<ValidationError> errors = new ArrayList<ValidationError>();
    if (errors.size() != 0) {
        return errors;
    }
    return null;
}

public List<Job> getJobs() {
    return this.jobs;
}

public List<Mail> getMails() {
    if (this.mails == null) {
        return null;
    }
    return this.mails;
}

public String getTestString() {
    return this.testString;
}

public String getUsername() {
    return this.username;
}

public void setJobs(final List<Job> jobs) {
    this.jobs = jobs;
}

public void setMails(final List<Mail> mails) {
    this.mails = mails;
}

public void setTestString(final String testString) {
    this.testString = testString;
}

public void setUsername(final String username) {
    this.username = username;
}

public boolean hasJob(Job job) {
    if (getJobs() == null) {
        return false;
    }
    return this.getJobs().contains(job);
}

@Override
public String toString() {
    final List<String> jobs = new ArrayList<String>();
    for (Job job : this.getJobs()) {
        jobs.add(job.getJobName());
    }

    final List<String> mails = new ArrayList<String>();
    for (final Mail mail : this.getMails()) {
        mails.add(mail.getMailAddr() + ", isMainMail: "
                + mail.getIsMainMail() + ", id: " + mail.getId());
    }

    return String
            .format("User [username=%s, testString=%s, jobs=%s, mails=%s, getCreated()=%s, getModified()=%s, getId()=%s]",
                    this.username, this.testString, jobs, mails,
                    this.getCreated(), this.getModified(), this.getId());
}


}
package models;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.validation.constraints.Null;

import play.Logger;
import play.Logger.ALogger;
import play.data.validation.Constraints.Email;
import play.data.validation.Constraints.Required;
import play.data.validation.ValidationError;

/**
 * Just a container for Emails. Because Play! cannot store List<String> for the
 * Email-Addresses
 * 
 * @author timmeey
 * 
 */
@Entity
public class Mail extends AbstractSuperModel {
private static final ALogger logger = Logger.of(Mail.class);

public static Mail findByAddr(final String address) {
    Mail mail = null;
    mail = find.where().eq("mailAddr", address).findUnique();
    return mail;

}

@Required
@Email
@javax.persistence.Column(unique = true)
private String mailAddr;

@Null
@ManyToOne
private User user;

@Null
Boolean isMainMail;

public static Finder<Long, Mail> find = new Finder<Long, Mail>(Long.class,
        Mail.class);

public static Mail findById(final Long id) {
    if (id == null) {
        return null;
    }
    return find.byId(id);
}

public static List<Mail> findAll() {
    return find.all();
}

public String getMailAddr() {
    return this.mailAddr;
}

public User getUser() {
    return this.user;
}

public void setMailAddr(final String mailAddr) {
    this.mailAddr = mailAddr;
}

public void setUser(final User user) {
    this.user = user;
}

@Override
public String toString() {
    return String
            .format("Mail [id=%s, mailAddr=%s, user=%s, getCreated()=%s, getModified()=%s, getId()=%s]",
                    this.id, this.mailAddr, this.user.getUsername(),
                    this.getCreated(), this.getModified(), this.getId());
}

public Boolean getIsMainMail() {
    return isMainMail;
}

public void setIsMainMail(Boolean isMainMail) {
    this.isMainMail = isMainMail;
}

public List<ValidationError> validate() {
    List<ValidationError> errors = new ArrayList<ValidationError>();

    if (find.where().eq("mailAddr", this.mailAddr).findRowCount() > 0) {
        errors.add(new ValidationError(mailAddr, "Mailaddress already used"));
    }
    if (errors.size() != 0) {
        return errors;
    }
    return null;

}

}