Java-сериализация объекта EJB, не сериализующая один элемент множества ManyToMany, но только иногда

#java #serialization #jboss #ejb

#java #сериализация #jboss #ejb

Вопрос:

Для процесса синхронизации между серверами я сериализуюобъекты сущности EJB по сети. Серверы — JBoss 5, использующие Hibernate для сохранения. В этом сценарии разработки мой удаленный сервер работает на Ubuntu Linux с sun-java6-jdk 1.6.0_24-b07, в то время как моя машина разработки работает под управлением OS X с Java 1.6.0_24-b07-334-10M3326.

Одним из моих классов является User, который может принадлежать к одной или нескольким группам. Проблема, с которой я сталкиваюсь, заключается в том, что для конкретного пользователя не все связанные объекты группы проходят через сериализацию: этот пользователь назначен двум группам, но проходит только одна. Другие пользователи справляются нормально, независимо от того, назначены ли они либо двум группам (аналогично затронутому пользователю), либо только отсутствующей группе или другой группе.

Пользователь является «владельцем» отношения ManyToMany и связан с группой следующим образом.

Пользователь

 public class User implements Serializable, Cloneable, DynamicEntity, Identifiable, Comparable<User> {
    // Other stuff omitted...

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "tbl_user_group", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "group_id", referencedColumnName = "id"))
    @XmlTransient
    private Set<Group> groups = new HashSet<Group>();
}
  

Группа

 public class Group implements Serializable, Identifiable, Comparable<Group> {
    // Other stuff omitted...

    @ManyToMany(mappedBy = "groups")
    @XmlTransient
    private Set<User> users = new TreeSet<User>();
}
  

На стороне удаленного сервера у меня есть служба, которая собирает объекты и возвращает их как List<?>[] . Вот метод service, который вызывается клиентами.

 public List<?>[] getSyncData() {
    final EntityManager em = emf.createEntityManager();
    List<?>[] data = null;
    try {
        data = getLocalData(em, toSlaveServices);
    } finally {
        em.close();
    }
    return data;
}
  

В методе getLocalData я регистрирую, какие объекты обрабатываются.

  User masteruser: [Group [899d07bf-28f9-437a-8811-6d53b8f2df5d, master62]]
 User admin: [Group [899d07bf-28f9-437a-8811-6d53b8f2df5d, master62], Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62]]
 User allgroups: [Group [899d07bf-28f9-437a-8811-6d53b8f2df5d, master62], Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62]]
 User slave62: [Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62]]
 User d8a0f514-0930-41fb-8028-5db9e8a2824c: [Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62]]
 User px: [Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62]]
 Sending [Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62], Group [899d07bf-28f9-437a-8811-6d53b8f2df5d, master62]]
  

Я включил всех пользователей и группы для контекста: тот, который меня интересует, — это пользователь allgroups в третьей строке. Как вы можете видеть, allgroups принадлежит двум группам, master62 и slave62.

На стороне клиента я также регистрирую, какие объекты я получаю обратно с сервера.

  User masteruser: [Group [899d07bf-28f9-437a-8811-6d53b8f2df5d, master62]]
 User admin: [Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62], Group [899d07bf-28f9-437a-8811-6d53b8f2df5d, master62]]
 User allgroups: [Group [899d07bf-28f9-437a-8811-6d53b8f2df5d, master62]]
 User slave62: [Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62]]
 User d8a0f514-0930-41fb-8028-5db9e8a2824c: [Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62]]
 User px: [Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62]]
 Received [Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62], Group [899d07bf-28f9-437a-8811-6d53b8f2df5d, master62]]
  

Как вы можете видеть, все проходит, как ожидалось, за исключением пользователя allgroups, который проходит с членством только в одной группе.

To see if I could track down what was going on, I decided to manually try serialization into files of the both the affected user object alone, and then the whole List array. Here’s the modified getSyncData method:

 public List<?>[] getSyncData() {
    final EntityManager em = emf.createEntityManager();
    List<?>[] data = null;
    try {
        data = getLocalData(em, toSlaveServices);
    } finally {
        em.close();
    }

    long time = System.currentTimeMillis();

    for (List<?> datum : data) {
        fgLogger.debug("Sending "   datum);
        if (datum != null amp;amp; datum.size() > 0 amp;amp; datum.get(0).getClass() == User.class) {
            for (User user : ((List<User>) datum)) {
                fgLogger.debug("User "   user.getUser_id()   ": "   user.getGroups());

                if ("allgroups".equals(user.getUser_id())) {
                    try {
                        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/tmp/user-"   time   ".ser"));
                        oos.writeObject(user);
                        oos.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    try {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/tmp/ssi-"   time   ".ser"));
        oos.writeObject(data);
        oos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return data;
}
  

Затем я скопировал два файла сериализации с удаленного сервера на свой клиент и попытался прочитать их с помощью простого скрипта Groovy (т. Е. не из JBoss), сначала файл user-xxx.ser, а затем файл ssi-xxx.ser.

 *** USER ***
User allgroups: [Group [899d07bf-28f9-437a-8811-6d53b8f2df5d, master62], Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62]]
*** SSI ***
User masteruser: [Group [899d07bf-28f9-437a-8811-6d53b8f2df5d, master62]]
User admin: [Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62], Group [899d07bf-28f9-437a-8811-6d53b8f2df5d, master62]]
User allgroups: [Group [899d07bf-28f9-437a-8811-6d53b8f2df5d, master62]]
User slave62: [Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62]]
User d8a0f514-0930-41fb-8028-5db9e8a2824c: [Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62]]
User px: [Group [0d293aa6-c5af-4a03-a8ce-edea9b05acb7, slave62]]
  

Результаты под битом SSI отражают мой опыт сериализации по сети. Однако только сериализованный пользователь содержит обе группы, как я хочу! Эти два файла сериализации были сгенерированы одним и тем же методом с использованием одних и тех же необработанных данных, хотя пользователь, очевидно, был вложен в другую структуру данных.

Как результаты для пользователя allgroups могут отличаться?? Я сбит с толку и был бы очень благодарен за любые разъяснения.

Ответ №1:

На случай, если кому-то интересно:

Я не нашел ответа на вопрос. Я подозреваю, что это как-то связано со способом хранения связанных групповых объектов в режиме гибернации PersistentSet (хотя PersistentSet и HashSet его поддержка являются сериализуемыми). Я отказался от отладки — время — деньги.

Я решил проблему, добавив дополнительное поле к моей пользовательской сущности, Set<String> groupIds . Я заполняю этот набор в User.writeObject , который вызывается, когда пользователь сериализуется. На принимающей стороне я очищаю набор групп, а затем вручную выполняю поиск групп на основе идентификаторов, сохраненных в поле groupIds. Затем я добавляю группы в набор групп пользователя. Это громоздко, и для этого требуется, чтобы я сначала синхронизировал группы, чтобы иметь возможность переназначать их — но это работает.