Обновление объектов в GAE

У меня есть проблема, которую я не могу решить. Я пытался искать в Интернете решения, но не нашел универсального решения. Я хочу обновить объект, каким бы ни был его класс, в хранилище данных. Для этого вот код, который я использую для проекта

Я использую DataNucleus и Google AppEngine.

Вот мой jdoconfig.xml

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">

   <persistence-manager-factory name="transactions-optional">
       <property name="javax.jdo.PersistenceManagerFactoryClass"
           value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
       <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
       <property name="javax.jdo.option.NontransactionalRead" value="true"/>
       <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
       <property name="javax.jdo.option.RetainValues" value="true"/>
       <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
   </persistence-manager-factory>
</jdoconfig>

Мой класс ПМФ

public final class PMF {
    private static final PersistenceManagerFactory pmfInstance =
        JDOHelper.getPersistenceManagerFactory("transactions-optional");

    private PMF() {}

    public static PersistenceManagerFactory get() {
        return pmfInstance;
    }

    public static PersistenceManager getPersistenceManager() {
        return pmfInstance.getPersistenceManager();
    }
}

Мой класс BaseModel, который является суперклассом для всех моделей проекта.

@Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
@PersistenceCapable(detachable = "true", identityType = IdentityType.APPLICATION)
public abstract class BaseModel implements Serializable {

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    protected Key id;

    public boolean equals(Object obj) {
        try {
            return obj instanceof BaseModel ? this.getId().getId() == ((BaseModel) obj).getId().getId() : false;
        } catch (Exception e) {
            return false;
        }
    }
}

Вот класс (Project), который я хочу сохранить

@PersistenceCapable(detachable = "true", identityType = IdentityType.APPLICATION)
public class Project extends BaseModel implements BeanModelTag {

    private static final long serialVersionUID = 3318013676594981107L;

    @Persistent
    private String name;

    @Persistent
    private String description;

    @Persistent
    private Date deadline;

    @Persistent
    private Integer status;

    @Persistent
    private Key manager;

    @Persistent
    private Set<Key> team;
}

Для этого я попробовал несколько вещей. Этот метод ниже успешно сохраняет НОВЫЙ экземпляр Project, но когда я вызываю для обновления уже отсоединенного объекта, обновляется только поле крайний срок, другие атрибуты не обновляются (и тем не менее, я перешел в режим отладки, чтобы проверить, были ли изменены другие атрибуты, и да, они были, но сохранен только крайний срок).

public void save(BaseModel object) {
    PersistenceManager pm = PMF.getPersistenceManager();
    try {
        pm.makePersistent(object);
    } finally {
        pm.close();
    }
}

Итак, я попробовал следующий код

public void update(Project object) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        pm.currentTransaction().begin();

        Project p = pm.getObjectById(Project.class, object.getId());
        p.setName(object.getName());
        p.setDeadline(object.getDeadline());
        p.setDescription(object.getDescription());
        p.setTeam(p.getTeam());
        p.setStatus(object.getStatus());

        pm.currentTransaction().commit();
    } catch (Exception e) {
        e.printStackTrace();
        pm.currentTransaction().rollback();
    } finally {
        pm.close();
    }
}

И это сработало. Хорошо, это работает таким образом, но мне нужен общий метод для всех моих моделей, поэтому я попробовал это

public void update(BaseModel object) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        pm.currentTransaction().begin();

        BaseModel ob = pm.getObjectById(object.getClass(), object.getId());

        for (Field f : ob.getClass().getDeclaredFields()) {
            if (!f.toString().contains("final")) {
                f.setAccessible(true);
                for (Field g : object.getClass().getDeclaredFields()) {
                    g.setAccessible(true);
                    if (f.getName().equals(g.getName())) {
                        f.set(ob, g.get(object));
                    }
                    g.setAccessible(false);
                }
            }
            f.setAccessible(false);
        }

        pm.makePersistent(ob);
        pm.currentTransaction().commit();
    } catch (Exception e) {
        e.printStackTrace();
        pm.currentTransaction().rollback();
    } finally {
        pm.close();
    }
}

Но это вообще не работает, ничего не сохраняется, и тем не менее, когда я System.out атрибуты вручную, они меняются. Я пробовал с pm.makePersistent(ob); и без него, но безрезультатно. Я не знаю, что делать. У меня есть 120 моделей в этом проекте, которые унаследованы от BaseModel, и я не могу найти способ сделать обновление, которое работает с моей моделью.

----------- Редактировать -----------

Спасибо за ответ. Вот мое решение на данный момент. Конечно, этот printStrackTree оттуда вылезет.

public void update(BaseModel object) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        pm.currentTransaction().begin();

        BaseModel ob = pm.getObjectById(object.getClass(), object.getId());

        for (Field f : ob.getClass().getDeclaredFields()) {
            if (!Pattern.compile("\\bfinal\\b").matcher(f.toString()).find()) {
                f.setAccessible(true);
                for (Field g : object.getClass().getDeclaredFields()) {
                    g.setAccessible(true);
                    if (f.getName().equals(g.getName())) {
                        f.set(ob, g.get(object));
                        JDOHelper.makeDirty(ob, f.getName());
                    }
                    g.setAccessible(false);
                }
                f.setAccessible(false);
            }
        }

        pm.makePersistent(object);
        pm.currentTransaction().commit();
    } catch (Exception e) {
        e.printStackTrace();
        pm.currentTransaction().rollback();
    } finally {
        pm.close();
    }
}

person SHiRKiT    schedule 20.09.2011    source источник


Ответы (3)


Использование отражения для обновления полей не будет обнаружено расширенными методами байт-кода JDO. Если вы хотите напрямую обновлять поля (будь то по полю или по отражению), вы можете позвонить

JDOHelper.makeDirty(obj, "myFieldName");

после выполнения вашего обновления, а затем изменение будет зарегистрировано JDO и, следовательно, обновлено в хранилище данных.

person DataNucleus    schedule 20.09.2011
comment
Эй, это то, что мне было нужно. Теперь у меня есть лучшее решение: всего 1 метод для всех моих объектов для сохранения/обновления объекта в хранилище данных. Просто чтобы все знали, у вас есть 2 варианта: - Или вы ставите отметку Dirty в каждом наборе, который не работает, что я не рекомендую. Я использую GXT, и способ работы GXT каким-то образом игнорирует JDO. - Или вы поместите это в метод сохранения на стороне сервера, что я и сделал и рекомендую. Вы можете проверить различия, но я обнаружил, что проще просто пометить все поля как грязные. - person SHiRKiT; 23.09.2011

Вы должны использовать JDOHelper при использовании отражения, чтобы убедиться, что ваши поля помечены как грязные.

person Community    schedule 09.01.2012

Да, проблемы с транзакциями. Если вы хотите иметь ГАРАНТИРОВАННОЕ сохранение модификации в данный момент времени, вы должны зафиксировать/сбросить транзакцию. У меня есть приложение GAE, использующее JPA, но у меня была точно такая же проблема.

Чтобы избежать шаблонного кода, я создал базовый класс для своего DAO, который берет объект Method, запускает транзакцию, выполняет этот метод и затем фиксирует его (или откатывает). Затем в моем дао я передаю обработчик метода и аргументы этому baseDao, чтобы иметь (полу) автоматическую обработку транзакций.

вы можете проверить код, если считаете, что это полезно:

BaseDAO: http://myprojects2.googlecode.com/svn/trunk/javakata_project/Javakata_v2/src/com/appspot/javakata6425/server/dao/BaseDAO.java

ArticleDAO : http://myprojects2.googlecode.com/svn/trunk/javakata_project/Javakata_v2/src/com/appspot/javakata6425/server/dao/ArticleDAO.java

person Shivan Dragon    schedule 20.09.2011
comment
Что ж, я использую методы begin и commit, чтобы избежать этого, но даже если это не удается. С фиксацией или без нее я получаю тот же результат, что и предыдущий, для всех показанных здесь методов. - person SHiRKiT; 20.09.2011
comment
Попробуйте использовать методы установки вместо полей в вашем коде, который выполняет самоанализ/рефлексию, чтобы установить значения в bean-компоненте. - person Shivan Dragon; 20.09.2011
comment
Но проблема в том, что если я это сделаю, то у меня не будет универсального метода для всех моих моделей. Как я уже сказал, я мог бы создать метод обновления для каждой имеющейся у меня модели, но я всеми силами хочу избежать этого. - person SHiRKiT; 20.09.2011
comment
Извините, я имею в виду, что в вашем общем коде, где вы выполняете for (Field f : ob.getClass().getDeclaredFields()) {, выполните метод m .... затем используйте методы установки для установки значений вместо поля... - person Shivan Dragon; 20.09.2011