在发生变化时进行监听并将其更新到数据库

时间:2017-09-23 21:53:32

标签: java mongodb task future morphia

所以我正在开发一个使用mongodb / morphia的软件,我需要一种方法来监听该对象以检查某个值何时更改以在数据库中更新它而不是手动执行它。

最好的方法是什么?观察者模式?

所以我有3个类:User(超类),GlobalPlayer和我的Main。 我知道我需要使用观察者模式但我在实施时遇到了问题。

用户类: User Class

GlobalPlayer类: GlobalPlayer Class

主要类别: enter image description here

正如你在我的主类上看到的那样,我每隔5秒更新一次我的GlobalPlayer,即使它没有任何新的更新,那么如何在这个中实现观察者模式呢?

2 个答案:

答案 0 :(得分:1)

使用脏标志。

使用观察者模式,您可以跟踪每个更改。但是,您是否真的想在每次进行一些改变时保存每个用户?可能不是,因此在使用观察者时,您可能还希望通过延迟写入来构建可以防止过高数据库负载的内容。此外,我总是试图保持我的 Dataobjects 简单,连接松散,因为这降低了复杂性。因此,我的猜测是观察者模式过度,使其比需要的更复杂。只需使用脏位和计划任务定期保存每个用户。系统崩溃时负载不大,数据丢失很少。

public class User {

  @Transient
  boolean dirty;

  private void markDirty() {
    dirty = true;
  }

  public void isDirty {
    return dirty();
  }

每个setter都应该首先调用 markDirty()方法。像这样:

public void addCoins(long coins) {
  markDirty();
  this.coins += coins;
}

在Global中,我们只在用户/他/她/其他人变脏时保存用户:

if (gp.isDirty()) {
  getPlayerManager().update(gp);
}

更好的设计会将if(gp.isDirty())移动到PlayerManager类的update,因为它应该由Playermanager负责保存。

答案 1 :(得分:0)

正如jjohns指出的那样,你可以使用ObserverObservable s

修改

他可能并不是那个意思。 从Java 9开始,不推荐使用此观察者模式。 一种替代方法是声明您自己的子类EventObject的事件:

class UserChangeEvent extends EventObject {
    private String actionCommand;  // additional property for more control
    public UserChangeEvent(Object source, String actionCommand) {
        super(source);
        this.actionCommand = actionCommand;
    }
    public String getActionCommand() { return actionCommand; }
}

然后,声明一个将处理事件的接口(onUserChange(UserChangeEvent)将是"回调"方法):

interface UserChangeListener {
    /**
     * Fired when any user property changes
     * @param event
     */
    public void onUserChange(UserChangeEvent event);
}

这是一个示例User类:

class User {
    private List<UserChangeListener> changeListeners = new ArrayList<UserChangeListener>();
    private String username;
    private String email;

    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }
    /**
     * Registers a UserChangeListener instance that will be notified when any setter is called
     * @param listener the new event listener
     */
    public void addUserChangeListener(UserChangeListener listener) {
        changeListeners.add(listener);
    }
    public String getUsername() { 
        return username; 
    }
    public void setUsername(String username) { 
        this.username = username;  // actually set the username
        UserChangeEvent event = new UserChangeEvent(this, "username");  // a new instance of our custom user change event
        // fire user change event for all event listeners
        for (UserChangeListener listener:changeListeners) {
            listener.onUserChange(event);
        }
    }
    public String getEmail() { 
        return email; 
    }
    public void setEmail(String email) { 
        this.email = email; 
        UserChangeEvent event = new UserChangeEvent(this, "email");
        for (UserChangeListener listener:changeListeners) {
            listener.onUserChange(event);
        }
    }   
}

以下是UserUserChangeEventUserChangeListener的简单实现:

public class Test {
    public static void main(String[] args) {
        User hworld1234 = new User("hworld1234", "hworld@example.com");
        hworld1234.addUserChangeListener(new UserChangeListener() {

            @Override
            public void onUserChange(UserChangeEvent event) {
                String actionCommand = event.getActionCommand();  // what we gave our custom event in the setters
                if ("username".equals(actionCommand)) {
                    System.out.println("username has been changed to '" + hworld1234.getUsername() + "'");
                } else if ("email".equals(actionCommand)) {
                    System.out.println("email has been changed to '" + hworld1234.getEmail() + "'");
                }
            }
        });
        hworld1234.setEmail("hworld@gmail.com");  // prints "email has been changed to 'hworld@gmail.com'"

    }   
}

如果您愿意,另一种方法是将每个属性的onXXXChange声明为&#34; bound&#34;忘了actionCommand属性;但是,如果在界面中甚至有多个方法,则应该将其转换为适配器。