Почему ObjectInput / OutputStream теряет ссылки на объекты при чтении и записи в файл? - PullRequest
0 голосов
/ 10 марта 2019

У меня есть два класса, которые взаимодействуют:

Полка, в которой хранятся CD, DVD, PaperMedias:

    public class Shelf {
        private ArrayList<CD> cds;
        private ArrayList<DVD> dvds;
        private ArrayList<PaperMedia> paperMedias;
          ...etc

И клиент:

public class Customer implements Serializable {
    private Map<PhysicalMedia, Calendar> mediaOwned;  
    private Map<PhysicalMedia,Calendar> mediaReturned; 
    private Map<PhysicalMedia,CalendarPeriod> mediaOnHold;
     ...etc

Физический носительродитель CD, DVD, PaperMedia.

Сначала я инициализирую полку с некоторыми предметами, а покупатель должен заимствовать некоторые из этих предметов.Затем я сохраняю эти объекты в ShelfObjects.txt и CustomerObjects.txt.

Когда я снова читаю эти объекты из этих файлов, создается впечатление, что связь между этими двумя объектами теряется, особенно между PhysicalMedia клиента и PhysicalMedia полки.,Они, безусловно, должны иметь ту же ссылку, например, Metallica CD должен иметь ту же ссылку на полке, что и в учетной записи клиента.

Так что, когда я изменяю, скажем, статус Metallica CD, он не будет обновлять его в другом источнике!

Есть ли место для сохранения этой ссылки?

Я загружаю и сохраняю носитель следующим образом в классе CustomerDatabase:

public class CustomersDatabase implements Serializable {

    private ArrayList<Customer> customers = new ArrayList<Customer>();
       //etc..

public void load() {
        try {
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("CustomersObject.txt"));

            try {
                customers.clear();
                while(true) {
                    customers.add((Customer)in.readObject());
                }
            } catch (ClassNotFoundException e) {
                System.out.println("Customer class in file wasn't found");

            }

            in.close();

        } catch (FileNotFoundException e) {
            System.out.println("File not found");
            e.printStackTrace();
        } catch (IOException e) {

            System.out.println("\n^ Customer load successful\n");


        }

public void save() {

    try {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("CustomersObject.txt",false));
        for (int i=0;i<customers.size();i++) {
            out.writeObject(customers.get(i));
            }


        out.close();

        System.out.println("\nCustomer save successful\n");

    } catch (FileNotFoundException e) {
        System.out.println("File not found");
        e.printStackTrace();
    } catch (IOException e) {
        System.out.println("IO exception ");
        e.printStackTrace();
    }

}

Я выполняю аналогичную загрузку и сохраняю в классе Shelf.

1 Ответ

2 голосов
/ 10 марта 2019

Проблема в том, что вы храните две базы данных, используя два отдельных файла, используя два отдельных потока сериализации объектов.

Причина, по которой ваш текущий подход не работает для вас, заключается в том, что протокол сериализации объектов хранит ссылки на объекты в виде простых чисел.Он нумерует объекты в потоке как 1, 2, 3 и т. Д. И использует эти номера для ссылки на ранее сериализованные объекты в потоке.

Это работает в контексте одного потока.Но когда у вас есть два или более потоков, у данного объекта могут быть разные номера в каждом потоке.Это означает, что у метода readObject нет способа узнать, что два объекта в двух разных потоках действительно должны быть связаны друг с другом.

Простое решение состоит в следующем:

ObjectInputStream in = new ObjectInputStream(new  FileInputStream("DB.txt"));
for (int i = 0; i < customers.size(); i++) {
    out.writeObject(customers.get(i));
}
for (int i = 0; i < shelves.size(); i++) {
    out.writeObject(shelves.get(i));
}

Или, что еще лучше, сериализовать списки customers и shelves напрямую, потому что это упростит десериализацию.

(Да ... это портит вам текущую структуру кода. Вам придетсязагрузите и сохраните базы данных Customer и Shelf в одном месте или передайте открытый поток по всему месту.)

Также возможно хранить объекты в двух отдельных файлах, но вам необходимоиспользовать пользовательские методы сериализации, чтобы сделать это.

  • Сначала вам нужно решить, в какой файл вы хотите поместить каждый общий дочерний объект;т.е. объекты PhysicalMedia хранятся в базе данных Customer или в базе данных Shelf?

  • Для каждого класса совместно используемых объектов определите поле идентификатора соответствующего типа;например, String или `long.Измените свою кодовую базу так, чтобы поле заполнялось значениями, которые являются уникальными в контексте приложения.

  • Для базы данных, в которой вы хотите разместить объекты, сериализуйте как обычно.

  • Для других баз данных используйте расширенные функциональные возможности Сериализации объектов, чтобы:

    • при записи, заменять объекты (только) их идентификаторами и
    • при чтении, найдите идентификаторы в первой базе данных и затем вставьте объект поиска в десериализованный объект.

Для последнего потребуется глубокое знание сериализации объектаAPI, пользовательские сериализации и так далее.В вашем примере у вас есть дополнительная сложность, что объекты, которые вы хотите заменить, являются ключами в Map объектах.Поскольку вы не можете добавить пользовательские readObject и writeObject для стандартного класса карты, вам нужно будет сделать глубокие трюки в пользовательских подклассах самих классов Stream.(И ... нет, я не могу привести вам пример!)


Кроме того: подобные вещи являются одной из причин, по которой нежелательно использовать сериализацию объектов для реализации базы данных.Лучше использовать реальную базу данных и технологию ORM, такую ​​как JPA (например, Hibernate).Есть и другие причины.

...