-1

I have two classes that interact:

Shelf, that stores CDs, DVDs, PaperMedias:

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

And Customer :

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

Physical media is a parent of CD,DVD,PaperMedia.

First I will initialize shelf with some items, and customer to have a few of these items borrowed. Then I save these objects to ShelfObjects.txt and CustomerObjects.txt.

When I read these objects once again from these files, it seems like link between these two is lost , specifically between PhysicalMedia of customer and PhysicalMedia of shelf. These should definitely have the same reference for example Metallica CD should have same reference in Shelf as in Customer's account.

So when i change say a status of Metallica CD, it wont update it in the other source!

Is there a was to preserve this reference ?

I load and save media in the following way in CustomerDatabase class:

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();
    }

}

I do similar load and store in Shelf class.

FabolousPotato
  • 65
  • 1
  • 10
  • 2
    Add method which you are using to save and read data from file. – Boken Mar 10 '19 at 00:29
  • 1
    in the title loose is typo, should be lose. – Lei Yang Mar 10 '19 at 00:33
  • I added the way i load and store those files – FabolousPotato Mar 10 '19 at 00:38
  • If you serialized the `Shelf` objects as well as the `Customer` objects into the file it would work. Deserialization creates a whole new set of objects. NB You don't need to serialize elements of a list individually. You can just serialize the list. And serialized data isn't text, and shouldn't be saved in a file with the .txt extension. – user207421 Mar 10 '19 at 00:53
  • So Shelf extends Serializable ? – FabolousPotato Mar 10 '19 at 00:54
  • 1
    So you should make it *implement* `Serializable` so you *can* serialize it into the file. – user207421 Mar 10 '19 at 00:54
  • Doesnt seem to work. What file format should I use other than txt? – FabolousPotato Mar 10 '19 at 01:03
  • *What* doesn't seem to work? NB You are already using a file format: serialized data. The file extension isn't a format, it's a name. People use ,dat, .ser, ... for this. – user207421 Mar 10 '19 at 01:21
  • The problem here is that you need to serialize everything to the same file, via the same `ObjectOutputStream`, and read it back via a single `ObjectInputStream`. – user207421 Mar 10 '19 at 01:38

1 Answers1

2

The problem is that you are storing your two databases using two separate files using two separate object serialization streams.

The reason that your current approach doesn't work for you is that the Object Serialization protocol stores object references as simple numbers. It numbers the objects in a stream as 1, 2, 3, and so on, and uses those numbers to refer to previously serialized objects in the stream.

This works within the context of one stream. But when you have two or more streams, a given object is likely to have different numbers in each stream. That means that there is no way for the readObject method to know that two objects in two different streams should actually be linked together.

The simple solution is to do is something like this:

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));
}

Or better still, serialize the customers and shelves lists directly, because that will simplify the deserialization.

(Yes ... this messes up you current code structure. You would have to load and save the Customer and Shelf databases in the same place, or pass an open stream around the place.)

It is also possible to store the objects in two separate files, but you need to use custom serialization methods to do it.

  • First you need to decide which file you want each shared child object to go into; i.e. do the PhysicalMedia objects get stored in the Customer database or the Shelf database?

  • For each shared object class, define an identifier field of an appropriate type; e.g. String or `long. Modify your code-base so that the field is populated with values that are unique in the context of the application.

  • For the database where you want to objects to live, serialize as normal.

  • For the other databases, use Object Serialization advanced functionality to:

    • on write, replace the objects with (just) their identifiers, and
    • on read, lookup the identifiers in the first database and then insert the object looked up into the deserialized object.

The last will require deep knowledge of the Object Serialization APIs, custom serialization, and so on. In your example, you have the added complexity that the objects you want to substitute are keys in Map objects. Since you can't add custom readObject and writeObject for a standard map class, you will need to do deep tricks in custom subclasses of the Stream classes themselves. (And ... no, I can't give you an example!)


Aside: this kind of thing is one of the reasons why it is inadvisable to use object serialization to implement a database. It is better to use a real database, and ORM technology such as JPA (e.g. Hibernate). There are other reasons too.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Thanks this is really helpful. Ill modify my code in such a way. Been trying to solve this the whole day. Database definitely sounds amazing however this is just a prototype system im trying to make so no need for such thing – FabolousPotato Mar 10 '19 at 02:15