How to specify object custom serialization in ORML

2020-04-14 07:07发布

I would like to store some field of type ParentClass as json string into my database. I don't want to use Serializable interface and DataType.SERIALIZABLE cause it ties with full class name of serialized class.

So I'm using the following code:

class ParentClass {

    @DatabaseField(persisterClass = MyFieldClassPersister.class)
    private MyFieldClass myField;
}

where persister class a kind of:

public class MyFieldClassPersister extends StringType {

    private static final MyFieldClassPersister singleTon = new MyFieldClassPersister();

    public static MyFieldClassPersister getSingleton() {
        return singleTon;
    }

    protected MyFieldClassPersister() {
        super(SqlType.STRING, new Class<?>[0]);
    }

    @Override
    public Object parseDefaultString(FieldType fieldType, String defaultStr) {
        return jsonStringToObject(defaultStr);
    }

    @Override
    public Object resultToSqlArg(FieldType fieldType, DatabaseResults results, int columnPos) throws SQLException {
        String string = results.getString(columnPos);
        return jsonStringToObject(string);
    }

    private static MyFieldClass jsonStringToObject(String string) {
        // json to object conversion logic
    }

}

Here are two issues I've met:

  1. I didn't get how to specify custom convertion from object to string. Seems that ORMLite calls Object.toString() in order to get string representation of the object. It would be great to have some method in Persister in which I could specify how to convert Object to string (json in my case). Yes, I can override toString() method in MyFieldClass, but it is more convenient to perform conversion in Persister. Is there any method I could override in order to specify convertion from model object to db-object?

  2. If I mark my custom field type as String type:

    class ParentClass {
    
        @DatabaseField(dataType = DataType.STRING, persisterClass = MyFieldClassPersister.class)
        private MyFieldClass myField;
    }
    

then ormlite crashes when saving object with the following message:

java.lang.IllegalArgumentException: Field class com.myapp.venue.MyFieldClass for
    field FieldType:name=myField,class=ParentClass is not valid for type
    com.j256.ormlite.field.types.StringType@272ed83b, maybe should be
    class java.lang.String

It doesn't crash if I omit dataType specification. Can I avoid this crash in some way? It seems to me that it's better to specify types explicitly.

2条回答
Anthone
2楼-- · 2020-04-14 07:22

So basically your persister should be implemented in the next way:

public class MyFieldClassPersister extends StringType {

    private static final MyFieldClassPersister INSTANCE = new MyFieldClassPersister();

    private MyFieldClassPersister() {
        super(SqlType.STRING, new Class<?>[] { MyFieldClass.class });
    }

    public static MyFieldClassPersister getSingleton() {
        return INSTANCE;
    }

    @Override
    public Object javaToSqlArg(FieldType fieldType, Object javaObject) {
        MyFieldClass myFieldClass = (MyFieldClass) javaObject;
        return myFieldClass != null ? getJsonFromMyFieldClass(myFieldClass) : null;
    }

    @Override
    public Object sqlArgToJava(FieldType fieldType, Object sqlArg, int columnPos) {
        return sqlArg != null ? getMyFieldClassFromJson((String) sqlArg) : null;
    }

    private String getJsonFromMyFieldClass(MyFieldClass myFieldClass) {
        // logic here
    }

    private MyFieldClass getMyFieldClassFromJson(String json) {
        // logic here
    }
}

You should register it in onCreate method of your OrmLiteSqliteOpenHelper class

    @Override
    public void onCreate(SQLiteDatabaseHolder holder, ConnectionSource connectionSource) {
            try {
                //...
                DataPersisterManager
                 .registerDataPersisters(MyFieldClassPersister.getSingleton());
            } catch (SQLException e) {
                // log exception
            }
    }

And then you can use it in your model like this:

@DatabaseField(persisterClass = MyFieldClassPersister.class, columnName = "column_name")
protected MyFieldClass myFieldClass;
查看更多
甜甜的少女心
3楼-- · 2020-04-14 07:33

Don't register the persister adapter in the onCreate() method. This method only gets called when your database is first created. You should add this somewhere else, like your constructor or onOpen() method.

查看更多
登录 后发表回答