Reducing boilerplate code for JSON mapping UserTypes

Jens Pelzetter 2020-05-23 16:30:01 +02:00
parent e5d0e5f329
commit 1f970809b3
2 changed files with 281 additions and 106 deletions

View File

@ -0,0 +1,163 @@
/*
* Copyright (C) 2020 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.libreccm.hibernate;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;
import org.libreccm.l10n.LocalizedString;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Objects;
import javax.json.Json;
import javax.json.JsonReader;
import javax.json.JsonWriter;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public abstract class AbstractCcmJsonUserType implements UserType {
@Override
public int[] sqlTypes() {
return new int[]{Types.JAVA_OBJECT};
}
@Override
public Class<LocalizedString> returnedClass() {
return LocalizedString.class;
}
@Override
public boolean equals(final Object obj1, final Object obj2)
throws HibernateException {
return Objects.equals(obj1, obj2);
}
@Override
public int hashCode(final Object obj) throws HibernateException {
return Objects.hashCode(obj);
}
@Override
public Object nullSafeGet(
final ResultSet resultSet,
final String[] names,
final SharedSessionContractImplementor session,
final Object owner
) throws HibernateException, SQLException {
final String cellContent = resultSet.getString(names[0]);
if (cellContent == null) {
return null;
} else {
try (StringReader strReader = new StringReader(cellContent);
JsonReader jsonReader = Json.createReader(strReader)) {
return nullSafeGet(jsonReader);
}
}
}
protected abstract Object nullSafeGet(final JsonReader jsonReader);
@Override
public void nullSafeSet(
final PreparedStatement statement,
final Object value,
final int index,
final SharedSessionContractImplementor session
) throws HibernateException, SQLException {
if (value == null) {
statement.setObject(index, null, Types.OTHER);
} else {
try (StringWriter strWriter = new StringWriter();
JsonWriter jsonWriter = Json.createWriter(strWriter)) {
nullSafeSet(value, jsonWriter);
statement.setObject(index, strWriter.toString(), Types.OTHER);
}catch (IOException ex) {
throw new HibernateException(ex);
}
}
}
protected abstract void nullSafeSet(
final Object value, final JsonWriter jsonWriter
);
@Override
public Object deepCopy(final Object value) throws HibernateException {
final byte[] serialized;
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(value);
oos.flush();
serialized = bos.toByteArray();
} catch (IOException ex) {
throw new HibernateException(ex);
}
final Object obj;
try (ByteArrayInputStream bais = new ByteArrayInputStream(serialized);
ObjectInputStream ois = new ObjectInputStream(bais)) {
obj = ois.readObject();
} catch (IOException | ClassNotFoundException ex) {
throw new HibernateException(ex);
}
return obj;
}
@Override
public boolean isMutable() {
return true;
}
@Override
public Serializable disassemble(final Object value)
throws HibernateException {
return (Serializable) deepCopy(value);
}
@Override
public Object assemble(final Serializable cached, final Object owner) throws
HibernateException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object replace(
final Object original,
final Object target,
final Object owner
) throws HibernateException {
return deepCopy(original);
}
}

View File

@ -21,6 +21,7 @@ package org.libreccm.l10n;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType; import org.hibernate.usertype.UserType;
import org.libreccm.hibernate.AbstractCcmJsonUserType;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -42,120 +43,131 @@ import javax.json.JsonReader;
import javax.json.JsonWriter; import javax.json.JsonWriter;
/** /**
* Hibernate User type mapping instances of {@link LocalizedString} to a JSON.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
public class LocalizedStringType implements UserType { public class LocalizedStringType extends AbstractCcmJsonUserType {
@Override @Override
public int[] sqlTypes() { protected Object nullSafeGet(final JsonReader jsonReader) {
return new int[]{Types.JAVA_OBJECT}; return LocalizedString.fromJson(jsonReader.readObject());
} }
@Override @Override
public Class<LocalizedString> returnedClass() { protected void nullSafeSet(
return LocalizedString.class; final Object value, final JsonWriter jsonWriter
) {
jsonWriter.writeObject(((LocalizedString) value).toJson());
} }
//implements UserType {
@Override // @Override
public boolean equals(final Object obj1, final Object obj2) // public int[] sqlTypes() {
throws HibernateException { // return new int[]{Types.JAVA_OBJECT};
return Objects.equals(obj1, obj2); // }
} //
// @Override
@Override // public Class<LocalizedString> returnedClass() {
public int hashCode(final Object obj) throws HibernateException { // return LocalizedString.class;
return Objects.hashCode(obj); // }
} //
// @Override
@Override // public boolean equals(final Object obj1, final Object obj2)
public Object nullSafeGet( // throws HibernateException {
final ResultSet resultSet, // return Objects.equals(obj1, obj2);
final String[] names, // }
final SharedSessionContractImplementor session, //
final Object owner // @Override
) throws HibernateException, SQLException { // public int hashCode(final Object obj) throws HibernateException {
final String cellContent = resultSet.getString(names[0]); // return Objects.hashCode(obj);
if (cellContent == null) { // }
return null; //
} else { // @Override
try (StringReader strReader = new StringReader(cellContent); // public Object nullSafeGet(
JsonReader jsonReader = Json.createReader(strReader)) { // final ResultSet resultSet,
return LocalizedString.fromJson(jsonReader.readObject()); // final String[] names,
} // final SharedSessionContractImplementor session,
} // final Object owner
} // ) throws HibernateException, SQLException {
// final String cellContent = resultSet.getString(names[0]);
@Override // if (cellContent == null) {
public void nullSafeSet( // return null;
final PreparedStatement statement, // } else {
final Object value, // try (StringReader strReader = new StringReader(cellContent);
final int index, // JsonReader jsonReader = Json.createReader(strReader)) {
final SharedSessionContractImplementor session // return LocalizedString.fromJson(jsonReader.readObject());
) throws HibernateException, SQLException { // }
if (value == null) { // }
statement.setObject(index, null, Types.OTHER); // }
} else { //
final JsonObject jsonObject = ((LocalizedString) value).toJson(); // @Override
try (StringWriter strWriter = new StringWriter(); // public void nullSafeSet(
JsonWriter jsonWriter = Json.createWriter(strWriter)) { // final PreparedStatement statement,
jsonWriter.writeObject(jsonObject); // final Object value,
statement.setObject(index, strWriter.toString(), Types.OTHER); // final int index,
} catch (IOException ex) { // final SharedSessionContractImplementor session
throw new HibernateException(ex); // ) throws HibernateException, SQLException {
} // if (value == null) {
} // statement.setObject(index, null, Types.OTHER);
} // } else {
// final JsonObject jsonObject = ((LocalizedString) value).toJson();
@Override // try (StringWriter strWriter = new StringWriter();
public Object deepCopy(final Object value) throws HibernateException { // JsonWriter jsonWriter = Json.createWriter(strWriter)) {
final byte[] serialized; // jsonWriter.writeObject(jsonObject);
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); // statement.setObject(index, strWriter.toString(), Types.OTHER);
ObjectOutputStream oos = new ObjectOutputStream(bos)) { // } catch (IOException ex) {
oos.writeObject(value); // throw new HibernateException(ex);
oos.flush(); // }
serialized = bos.toByteArray(); // }
} catch (IOException ex) { // }
throw new HibernateException(ex); //
} // @Override
// public Object deepCopy(final Object value) throws HibernateException {
final Object obj; // final byte[] serialized;
try (final ByteArrayInputStream bais = new ByteArrayInputStream( // try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
serialized // ObjectOutputStream oos = new ObjectOutputStream(bos)) {
); // oos.writeObject(value);
final ObjectInputStream ois = new ObjectInputStream(bais);) { // oos.flush();
obj = ois.readObject(); // serialized = bos.toByteArray();
} catch (IOException | ClassNotFoundException ex) { // } catch (IOException ex) {
throw new HibernateException(ex); // throw new HibernateException(ex);
} // }
//
return obj; // final Object obj;
} // try (ByteArrayInputStream bais = new ByteArrayInputStream(serialized);
// ObjectInputStream ois = new ObjectInputStream(bais)) {
@Override // obj = ois.readObject();
public boolean isMutable() { // } catch (IOException | ClassNotFoundException ex) {
return true; // throw new HibernateException(ex);
} // }
// return obj;
@Override // }
public Serializable disassemble(final Object value) //
throws HibernateException { // @Override
return (Serializable) deepCopy(value); // public boolean isMutable() {
} // return true;
// }
@Override //
public Object assemble(final Serializable cached, final Object owner) throws // @Override
HibernateException { // public Serializable disassemble(final Object value)
throw new UnsupportedOperationException("Not supported yet."); // throws HibernateException {
} // return (Serializable) deepCopy(value);
// }
@Override //
public Object replace( // @Override
final Object original, // public Object assemble(final Serializable cached, final Object owner) throws
final Object target, // HibernateException {
final Object owner // throw new UnsupportedOperationException("Not supported yet.");
) throws HibernateException { // }
return deepCopy(original); //
} // @Override
// public Object replace(
// final Object original,
// final Object target,
// final Object owner
// ) throws HibernateException {
// return deepCopy(original);
// }
} }