From 1f970809b3df137f469d1888278b0709d73d2960 Mon Sep 17 00:00:00 2001 From: Jens Pelzetter Date: Sat, 23 May 2020 16:30:01 +0200 Subject: [PATCH] Reducing boilerplate code for JSON mapping UserTypes --- .../hibernate/AbstractCcmJsonUserType.java | 163 +++++++++++++ .../libreccm/l10n/LocalizedStringType.java | 224 +++++++++--------- 2 files changed, 281 insertions(+), 106 deletions(-) create mode 100644 ccm-core/src/main/java/org/libreccm/hibernate/AbstractCcmJsonUserType.java diff --git a/ccm-core/src/main/java/org/libreccm/hibernate/AbstractCcmJsonUserType.java b/ccm-core/src/main/java/org/libreccm/hibernate/AbstractCcmJsonUserType.java new file mode 100644 index 000000000..64af95d3a --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/hibernate/AbstractCcmJsonUserType.java @@ -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 Jens Pelzetter + */ +public abstract class AbstractCcmJsonUserType implements UserType { + + @Override + public int[] sqlTypes() { + return new int[]{Types.JAVA_OBJECT}; + } + + @Override + public Class 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); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/l10n/LocalizedStringType.java b/ccm-core/src/main/java/org/libreccm/l10n/LocalizedStringType.java index 1681f46ac..30d2dc220 100644 --- a/ccm-core/src/main/java/org/libreccm/l10n/LocalizedStringType.java +++ b/ccm-core/src/main/java/org/libreccm/l10n/LocalizedStringType.java @@ -21,6 +21,7 @@ package org.libreccm.l10n; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.usertype.UserType; +import org.libreccm.hibernate.AbstractCcmJsonUserType; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -42,120 +43,131 @@ import javax.json.JsonReader; import javax.json.JsonWriter; /** - * + * Hibernate User type mapping instances of {@link LocalizedString} to a JSON. + * * @author Jens Pelzetter */ -public class LocalizedStringType implements UserType { +public class LocalizedStringType extends AbstractCcmJsonUserType { @Override - public int[] sqlTypes() { - return new int[]{Types.JAVA_OBJECT}; + protected Object nullSafeGet(final JsonReader jsonReader) { + return LocalizedString.fromJson(jsonReader.readObject()); } @Override - public Class returnedClass() { - return LocalizedString.class; + protected void nullSafeSet( + final Object value, final JsonWriter jsonWriter + ) { + jsonWriter.writeObject(((LocalizedString) value).toJson()); } + //implements UserType { - @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 LocalizedString.fromJson(jsonReader.readObject()); - } - } - } - - @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 { - final JsonObject jsonObject = ((LocalizedString) value).toJson(); - try (StringWriter strWriter = new StringWriter(); - JsonWriter jsonWriter = Json.createWriter(strWriter)) { - jsonWriter.writeObject(jsonObject); - statement.setObject(index, strWriter.toString(), Types.OTHER); - } catch (IOException ex) { - throw new HibernateException(ex); - } - } - } - - @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 (final ByteArrayInputStream bais = new ByteArrayInputStream( - serialized - ); - final 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); - } +// @Override +// public int[] sqlTypes() { +// return new int[]{Types.JAVA_OBJECT}; +// } +// +// @Override +// public Class 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 LocalizedString.fromJson(jsonReader.readObject()); +// } +// } +// } +// +// @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 { +// final JsonObject jsonObject = ((LocalizedString) value).toJson(); +// try (StringWriter strWriter = new StringWriter(); +// JsonWriter jsonWriter = Json.createWriter(strWriter)) { +// jsonWriter.writeObject(jsonObject); +// statement.setObject(index, strWriter.toString(), Types.OTHER); +// } catch (IOException ex) { +// throw new HibernateException(ex); +// } +// } +// } +// +// @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); +// } }