diff --git a/src/main/java/com/karakays/hibernate/array/BaseEnumArrayType.java b/src/main/java/com/karakays/hibernate/array/BaseEnumArrayType.java new file mode 100644 index 0000000..6b7e2ff --- /dev/null +++ b/src/main/java/com/karakays/hibernate/array/BaseEnumArrayType.java @@ -0,0 +1,97 @@ +/** + * Copyright (C) 2018 Selçuk Karakayalı (skarakayali@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.karakays.hibernate.array; + +import org.hibernate.HibernateException; +import org.hibernate.usertype.ParameterizedType; +import org.hibernate.usertype.UserType; + +import java.io.Serializable; +import java.sql.Types; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Properties; + +/** + * Base class for array type that persists a list of custom enums + * + * @author Selçuk Karakayalı + */ +public abstract class BaseEnumArrayType implements UserType, ParameterizedType { + protected final int[] arrayTypes = new int[] { Types.ARRAY }; + + protected Class> mappedClass; + + protected void setMappedClass(Class> mappedClass) { + this.mappedClass = mappedClass; + } + + protected Class> getMappedClass() { + return mappedClass; + } + + public int[] sqlTypes() { + return arrayTypes; + } + + public Class returnedClass() { + return List.class; + } + + public boolean equals(Object x, Object y) throws HibernateException { + return Objects.equals(x, y); + } + + public int hashCode(Object x) throws HibernateException { + return x == null ? 0 : x.hashCode(); + } + + public Object deepCopy(Object value) throws HibernateException { + if (value == null) { + return null; + } + + return new ArrayList<>((List>) value); + } + + public boolean isMutable() { + return false; + } + + public Serializable disassemble(Object value) throws HibernateException { + return (Serializable) value; + } + + public Object assemble(Serializable cached, Object owner) throws HibernateException { + return cached; + } + + public Object replace(Object original, Object target, Object owner) throws HibernateException { + return original; + } + + public void setParameterValues(Properties parameters) { + if (parameters.containsKey("enumClass")) { + String enumClassName = parameters.getProperty("enumClass"); + try { + setMappedClass((Class>) Class.forName(enumClassName)); + } catch (ClassNotFoundException e) { + throw new HibernateException("Specified enum class could not be found", e); + } + } + } +} diff --git a/src/main/java/com/karakays/hibernate/array/EnumArrayType.java b/src/main/java/com/karakays/hibernate/array/EnumArrayType.java index b73a021..53dc771 100644 --- a/src/main/java/com/karakays/hibernate/array/EnumArrayType.java +++ b/src/main/java/com/karakays/hibernate/array/EnumArrayType.java @@ -15,85 +15,22 @@ */ package com.karakays.hibernate.array; -import java.io.Serializable; +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; + import java.sql.Array; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Types; import java.util.ArrayList; import java.util.List; -import java.util.Properties; - -import org.hibernate.HibernateException; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.usertype.ParameterizedType; -import org.hibernate.usertype.UserType; /** - * Array type that persists a list of custom enums + * Array type that persists a list of custom enums as string * * @author Selçuk Karakayalı */ -public class EnumArrayType implements UserType, ParameterizedType { - private final int[] arrayTypes = new int[] { Types.ARRAY }; - - private Class> mappedClass; - - protected void setMappedClass(Class> mappedClass) { - this.mappedClass = mappedClass; - } - - protected Class> getMappedClass() { - return mappedClass; - } - - public int[] sqlTypes() { - return arrayTypes; - } - - public Class returnedClass() { - return List.class; - } - - public boolean equals(Object x, Object y) throws HibernateException { - return x == null ? y == null : x.equals(y); - } - - public int hashCode(Object x) throws HibernateException { - return x == null ? 0 : x.hashCode(); - } - - public Object deepCopy(Object value) throws HibernateException { - if (value == null) { - return null; - } - - List> list = (List>) value; - ArrayList> clone = new ArrayList>(); - for (Enum intOn : list) { - clone.add(intOn); - } - - return clone; - } - - public boolean isMutable() { - return false; - } - - public Serializable disassemble(Object value) throws HibernateException { - return (Serializable) value; - } - - public Object assemble(Serializable cached, Object owner) throws HibernateException { - return cached; - } - - public Object replace(Object original, Object target, Object owner) throws HibernateException { - return original; - } - +public class EnumArrayType extends BaseEnumArrayType { public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { if (names != null && names.length > 0 && rs != null && rs.getArray(names[0]) != null) { @@ -113,22 +50,11 @@ public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSes throws HibernateException, SQLException { if (value != null && st != null) { List> list = (List>) value; - String[] castObject = (String[]) list.stream().map(e -> e.name()).toArray(String[]::new); + String[] castObject = list.stream().map(Enum::name).toArray(String[]::new); Array array = session.connection().createArrayOf("text", castObject); st.setArray(index, array); } else { st.setNull(index, arrayTypes[0]); } } - - public void setParameterValues(Properties parameters) { - if (parameters.containsKey("enumClass")) { - String enumClassName = parameters.getProperty("enumClass"); - try { - setMappedClass((Class>) Class.forName(enumClassName)); - } catch (ClassNotFoundException e) { - throw new HibernateException("Specified enum class could not be found", e); - } - } - } } diff --git a/src/main/java/com/karakays/hibernate/array/EnumIntArrayType.java b/src/main/java/com/karakays/hibernate/array/EnumIntArrayType.java new file mode 100644 index 0000000..853438a --- /dev/null +++ b/src/main/java/com/karakays/hibernate/array/EnumIntArrayType.java @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2018 Selçuk Karakayalı (skarakayali@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.karakays.hibernate.array; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; + +import java.sql.Array; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * Array type that persists a list of custom enums as integer + * + * @author Selçuk Karakayalı + */ +public class EnumIntArrayType extends BaseEnumArrayType { + public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) + throws HibernateException, SQLException { + if (names != null && names.length > 0 && rs != null && rs.getArray(names[0]) != null) { + Integer[] values = ((Integer[]) rs.getArray(names[0]).getArray()); + + List> enumList = new ArrayList<>(); + + for (Integer value : values) { + for (Enum enumConstant : mappedClass.getEnumConstants()) { + if (enumConstant.ordinal() == value) { + enumList.add(enumConstant); + } + } + } + return enumList; + } + return null; + } + + public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) + throws HibernateException, SQLException { + if (value != null && st != null) { + List> list = (List>) value; + Integer[] castObject = list.stream().map(e -> { + if (e == null) { + return null; + } else { + return e.ordinal(); + } + }).toArray(Integer[]::new); + Array array = session.connection().createArrayOf("int", castObject); + st.setArray(index, array); + } else { + st.setNull(index, arrayTypes[0]); + } + } +} diff --git a/src/test/java/com/karakays/hibernate/array/HibernateCustomTypeTest.java b/src/test/java/com/karakays/hibernate/array/HibernateCustomTypeTest.java index 42b2383..61b1ef0 100644 --- a/src/test/java/com/karakays/hibernate/array/HibernateCustomTypeTest.java +++ b/src/test/java/com/karakays/hibernate/array/HibernateCustomTypeTest.java @@ -15,14 +15,8 @@ */ package com.karakays.hibernate.array; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.hasItems; - -import java.io.Serializable; -import java.util.Arrays; - +import com.karakays.hibernate.array.domain.Item; +import com.karakays.hibernate.array.domain.User; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.boot.Metadata; @@ -34,8 +28,13 @@ import org.junit.BeforeClass; import org.junit.Test; -import com.karakays.hibernate.array.domain.Item; -import com.karakays.hibernate.array.domain.User; +import java.io.Serializable; +import java.util.Arrays; + +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.hasItems; public class HibernateCustomTypeTest { private static SessionFactory sessionFactory; @@ -108,29 +107,34 @@ public void shouldLoadUser() { User loadedUser = (User) load(User.class, user1.getId()); assertThat(loadedUser.getBadges(), hasItems(User.Badge.MASTER)); + assertThat(loadedUser.getBadgesAsInt(), hasItems(User.Badge.MASTER)); } @Test public void shouldUpdateProperty() { Item item = (Item) load(Item.class, item2); item.setProperties(Arrays.asList(Item.Property.ALL)); - + item.setPropertiesAsInt(Arrays.asList(Item.Property.ALL)); + save(item); item = load(Item.class, item2); assertThat(item.getProperties(), hasItems(Item.Property.ALL)); + assertThat(item.getPropertiesAsInt(), hasItems(Item.Property.ALL)); } @Test public void shouldDeleteProperty() { Item item = (Item) load(Item.class, item2); - item.setProperties(null);; + item.setProperties(null); + item.setPropertiesAsInt(null); update(item); item = (Item) load(Item.class, item2); assertThat(item.getProperties(), nullValue()); + assertThat(item.getPropertiesAsInt(), nullValue()); } @SuppressWarnings("unchecked") diff --git a/src/test/java/com/karakays/hibernate/array/domain/Item.java b/src/test/java/com/karakays/hibernate/array/domain/Item.java index 47b407f..fa8f4bf 100644 --- a/src/test/java/com/karakays/hibernate/array/domain/Item.java +++ b/src/test/java/com/karakays/hibernate/array/domain/Item.java @@ -15,27 +15,13 @@ */ package com.karakays.hibernate.array.domain; -import java.util.Arrays; -import java.util.List; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; - +import lombok.*; import org.hibernate.annotations.Parameter; import org.hibernate.annotations.Type; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import javax.persistence.*; +import java.util.Arrays; +import java.util.List; @AllArgsConstructor @NoArgsConstructor(access=AccessLevel.PRIVATE) @@ -54,9 +40,13 @@ public class Item { @Type(type = "com.karakays.hibernate.array.EnumArrayType", parameters = { @Parameter(name="enumClass", value="com.karakays.hibernate.array.domain.Item$Property") }) private List properties; - + @Column(name = "properties_as_int", columnDefinition="int[]") + @Type(type = "com.karakays.hibernate.array.EnumIntArrayType", + parameters = { @Parameter(name="enumClass", value="com.karakays.hibernate.array.domain.Item$Property") }) + private List propertiesAsInt; + public Item(User user, Property... properties) { - this(null, user, Arrays.asList(properties)); + this(null, user, Arrays.asList(properties), Arrays.asList(properties)); } public enum Property { diff --git a/src/test/java/com/karakays/hibernate/array/domain/User.java b/src/test/java/com/karakays/hibernate/array/domain/User.java index 6cb23b2..771006f 100644 --- a/src/test/java/com/karakays/hibernate/array/domain/User.java +++ b/src/test/java/com/karakays/hibernate/array/domain/User.java @@ -15,26 +15,16 @@ */ package com.karakays.hibernate.array.domain; -import java.util.Arrays; -import java.util.List; - -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.OneToMany; -import javax.persistence.Table; - -import org.hibernate.annotations.Parameter; -import org.hibernate.annotations.Type; - import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Parameter; +import org.hibernate.annotations.Type; + +import javax.persistence.*; +import java.util.Arrays; +import java.util.List; @AllArgsConstructor @NoArgsConstructor(access=AccessLevel.PRIVATE) @@ -52,6 +42,10 @@ public class User { @Type(type = "com.karakays.hibernate.array.EnumArrayType", parameters = { @Parameter(name="enumClass", value="com.karakays.hibernate.array.domain.User$Badge") }) private List badges; + @Column(name = "badges_as_int", columnDefinition="int[]") + @Type(type = "com.karakays.hibernate.array.EnumIntArrayType", + parameters = { @Parameter(name="enumClass", value="com.karakays.hibernate.array.domain.User$Badge") }) + private List badgesAsInt; public User(String name) { this.name = name; @@ -60,6 +54,7 @@ public User(String name) { public User(String name, Badge... badges) { this(name); this.badges = Arrays.asList(badges); + this.badgesAsInt = Arrays.asList(badges); } public enum Badge {