Storing an object containing a Calender object in a db4o local database, cannot retrieve object fields

烂漫一生 提交于 2019-12-12 20:24:48

问题


I have been storing an object which contains a GregorianCalendar object in a db4o database, which works just fine. However, on retrieving the object (after closing and re-opening the database), I cannot seem to access some of the information inside (namely get(GregorianCalendar.MONTH) ). I have included test code below, and am wondering how to fix this problem.

import static org.junit.Assert.assertEquals;

import java.util.GregorianCalendar;

import org.junit.Test;

import com.db4o.Db4oEmbedded;
import com.db4o.ObjectContainer;
import com.db4o.config.EmbeddedConfiguration;

public class DateTest {

    public class RecordDate {

        private GregorianCalendar calendar;

        public RecordDate() {
            calendar = new GregorianCalendar();
        }

        public int getMonth() {
            return calendar.get(GregorianCalendar.MONTH);
        }
    }

    @Test
    public void testGetMonth() {
        EmbeddedConfiguration config = Db4oEmbedded.newConfiguration();
        config.common().objectClass(RecordDate.class).cascadeOnActivate(true);
        config.common().objectClass(RecordDate.class).cascadeOnUpdate(true);
        config.common().activationDepth(25);
        config.common().updateDepth(25);

        ObjectContainer database = Db4oEmbedded.openFile(config,
                "db/datetest.db");

        GregorianCalendar currentdate = new GregorianCalendar();
        RecordDate testdate = new RecordDate();
        assertEquals(currentdate.get(GregorianCalendar.MONTH),
                testdate.getMonth()); // this passes

        database.store(testdate);
        database.close();

        EmbeddedConfiguration config2 = Db4oEmbedded.newConfiguration();
        config2.common().objectClass(RecordDate.class).cascadeOnActivate(true);
        config2.common().objectClass(RecordDate.class).cascadeOnUpdate(true);
        config2.common().activationDepth(25);
        config2.common().updateDepth(25);
        database = Db4oEmbedded.openFile(config2, "db/datetest.db");

        testdate = (RecordDate) database.queryByExample(RecordDate.class)
                .next();
        assertEquals(currentdate.get(GregorianCalendar.MONTH),
                testdate.getMonth()); // this should pass, but doesn't
        database.close();
    }
}

回答1:


Db4o does support java.util.Calendar objects, but not out-of-the-box. To add support for Calendar, add one of these two lines to your database configuration:

configuration.common().objectClass(Calendar.class).callConstructor(true);

or

configuration.common().objectClass(Calendar.class).storeTransientFields(true);

The reason this is necessary is that Calendar has transient fields, or fields that don't get stored or serialized by default in Java (JLS §8.3.1.3). You can find these fields in the Calendar source code if you want. These fields are usually dependent on some other field in the object and get calculated when the value of the other field changes. From the JLS section above:

Variables may be marked transient to indicate that they are not part of the persistent state of an object.

For example, let's say I have a class that represents a sale:

public class Sale {
    private double cost, taxRate;
    private transient double taxesPaid;

    // etc...
}

I've declared the taxesPaid variable here as transient because I can figure it out from cost and taxRate. If the cost is $2.00 and the tax rate is 7%, then the taxes paid would be $0.14. In databases, this kind of information is usually considered redundant, and there are entire books written on how to remove this kind of dependency from a database. In Java, you can specify that the field not be stored at all by specifying it as transient.

Db4o honors Java's definition of transient fields, and it won't store them either. In fact, most persistence frameworks and databases won't store transient fields. So when db4o recreates the Calendar object from the database, it only restores its non-transient fields, which means all the transient fields are null (hence your NullPointerException). To fix this, you tell db4o to either:

  • Call Calendar's constructor, and the Calendar constructor will initialize/calculate the transient fields automatically or
  • Store Calendar's transient fields, which will treat the fields as if they aren't transient.

For the record, I've seen people recommend using both lines in their code, and I've also seen GregorianCalendar.class in place of Calendar.class. One or the other should work fine though.

Sorry for weighing in late. I just thought it was important to make sure anyone reading this knows this is the proper way to store the Calendar (it allows indexing, querying, etc. on the Calendar fields without object instantiation slowing down the queries).




回答2:


db4o doesn't support storing Calendar instances. You should store Date instances.

As alternative you can use an object translator to store your calendar object. However is will not support any query on that object. You could try the serializing translator, which just uses Java serialization to store the object:

configuration.common().objectClass(Calendar.class).translate(new TSerializable());
configuration.common().objectClass(GregorianCalendar.class).translate(new TSerializable());

db4o basically supports storing your own 'data'-object which can consist of primitives, strings, arrays, basic collection and references to other data-objects. However db4o doesn't support storing complex framework objects, like Calendar, Swing-Objects and concurrent collections etc.

However db4o tries to store any object, no matter what. However with complex object with tons of transient state and references to static objects this can fail.



来源:https://stackoverflow.com/questions/8014691/storing-an-object-containing-a-calender-object-in-a-db4o-local-database-cannot

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!