Skip to content

Instantly share code, notes, and snippets.

@azami
Created September 8, 2020 07:08
Show Gist options
  • Save azami/68d72ff985883510d603dd10c7156890 to your computer and use it in GitHub Desktop.
Save azami/68d72ff985883510d603dd10c7156890 to your computer and use it in GitHub Desktop.
PostgreSQL + JPA + Hibernate Set<Enum> (Collection) Descriptor
import com.vladmihalcea.hibernate.type.array.internal.AbstractArrayTypeDescriptor
import com.vladmihalcea.hibernate.type.array.internal.ArrayUtil
import org.hibernate.type.descriptor.WrapperOptions
import org.hibernate.type.descriptor.java.MutableMutabilityPlan
class CollectionMutableMutabilityPlan<T> : MutableMutabilityPlan<Collection<T>?>() {
override fun deepCopyNotNull(value: Collection<T>?): Collection<T>? {
return value?.map { it }
}
}
@Suppress("UNCHECKED_CAST")
abstract class CollectionTypeDescriptor<T>(private val collection: Collection<*>) :
AbstractArrayTypeDescriptor<Collection<T>>(
emptyList<T>()::class.java as Class<Collection<T>>,
CollectionMutableMutabilityPlan<T>()
) {
abstract fun toSQLValue(value: T): String
abstract fun toValue(value: String): T
override fun <X : Any?> unwrap(value: Collection<T>?, type: Class<X>?, options: WrapperOptions?): X? {
if (value == null) return null
return value.map { toSQLValue(it) }.toTypedArray() as X
}
private fun <T> format(values: List<T>): Collection<T> {
return when (collection) {
is Set<*> -> values.toSet()
is List<*> -> values
else ->
throw UnsupportedOperationException("The $collection is not supported yet!")
}
}
override fun <X : Any?> wrap(value: X, options: WrapperOptions?): Collection<T>? {
if (value == null) return null
return when (value) {
is org.postgresql.jdbc.PgArray -> {
val values = (value.array as Array<String>).map { toValue(it) }
format(values)
}
else -> value.run {
throw UnsupportedOperationException("The ${this::class.java} is not supported yet!")
}
}
}
override fun areEqual(one: Any?, another: Any?): Boolean {
return one == another
}
override fun fromString(string: String?): Collection<T>? {
val converted = ArrayUtil.fromString(string, emptyArray<String>()::class.java)
return listOf(*converted).map { toValue(it) }
}
}
@Entity
@Table(name = "foo_table")
@TypeDefs(
value = [
TypeDef(name = "excamples", typeClass = ExamplesDialect::class),
]
)
data class InsertionOrder(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int?,
@Column
@get:NotNull
@get:Size(min = 1, max = 255)
var name: String?,
@Column
@Type(type = "examples")
var examples: MutableSet<Example>?,
)
class ExamplesDialect:
AbstractHibernateType<Collection<Example>>(ArraySqlTypeDescriptor.INSTANCE, ExamplesDescriptor()),
DynamicParameterizedType {
override fun registerUnderJavaType(): Boolean {
return true
}
override fun setParameterValues(parameters: Properties) {
}
companion object {
val INSTANCE = ExamplesDialect()
}
override fun getName(): String {
return "examples"
}
class ExamplesDescriptor : CollectionTypeDescriptor<Example>(emptySet<Example>()) {
override fun toSQLValue(value: Example): String {
return value.name.toLowerCase()
}
override fun toValue(value: String): Example {
return Example.valueOf(value.toUpperCase())
}
override fun getSqlArrayType(): String {
return "channel"
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment