mirror of
https://github.com/google/nomulus.git
synced 2025-07-10 13:13:28 +02:00
Auto-apply JPA converters for collection type (#469)
* Auto-apply JPA converters for collection type * Extract common logic to a base class * Remove extra lines * Rebase on master
This commit is contained in:
parent
736f788eea
commit
594ce30122
21 changed files with 408 additions and 261 deletions
|
@ -53,7 +53,6 @@ import java.util.concurrent.ExecutionException;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.MappedSuperclass;
|
import javax.persistence.MappedSuperclass;
|
||||||
import javax.persistence.Transient;
|
import javax.persistence.Transient;
|
||||||
import org.hibernate.annotations.Type;
|
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.joda.time.Duration;
|
import org.joda.time.Duration;
|
||||||
|
|
||||||
|
@ -117,7 +116,6 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
||||||
DateTime lastEppUpdateTime;
|
DateTime lastEppUpdateTime;
|
||||||
|
|
||||||
/** Status values associated with this resource. */
|
/** Status values associated with this resource. */
|
||||||
@Type(type = "google.registry.model.eppcommon.StatusValue$StatusValueSetType")
|
|
||||||
@Column(name = "statuses")
|
@Column(name = "statuses")
|
||||||
// TODO(mmuller): rename to "statuses" once we're off datastore.
|
// TODO(mmuller): rename to "statuses" once we're off datastore.
|
||||||
Set<StatusValue> status;
|
Set<StatusValue> status;
|
||||||
|
|
|
@ -185,7 +185,6 @@ public class DomainBase extends EppResource
|
||||||
String idnTableName;
|
String idnTableName;
|
||||||
|
|
||||||
/** Fully qualified host names of this domain's active subordinate hosts. */
|
/** Fully qualified host names of this domain's active subordinate hosts. */
|
||||||
@org.hibernate.annotations.Type(type = "google.registry.persistence.StringSetUserType")
|
|
||||||
Set<String> subordinateHosts;
|
Set<String> subordinateHosts;
|
||||||
|
|
||||||
/** When this domain's registration will expire. */
|
/** When this domain's registration will expire. */
|
||||||
|
|
|
@ -25,7 +25,6 @@ import google.registry.model.domain.DomainBase;
|
||||||
import google.registry.model.host.HostResource;
|
import google.registry.model.host.HostResource;
|
||||||
import google.registry.model.translators.EnumToAttributeAdapter.EppEnum;
|
import google.registry.model.translators.EnumToAttributeAdapter.EppEnum;
|
||||||
import google.registry.model.translators.StatusValueAdapter;
|
import google.registry.model.translators.StatusValueAdapter;
|
||||||
import google.registry.persistence.EnumSetUserType;
|
|
||||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -166,6 +165,4 @@ public enum StatusValue implements EppEnum {
|
||||||
return StatusValue.valueOf(LOWER_CAMEL.to(UPPER_UNDERSCORE, nullToEmpty(xmlName)));
|
return StatusValue.valueOf(LOWER_CAMEL.to(UPPER_UNDERSCORE, nullToEmpty(xmlName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Hibernate type for sets of {@link StatusValue}. */
|
|
||||||
public static class StatusValueSetType extends EnumSetUserType<StatusValue> {}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -262,15 +262,12 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
State state;
|
State state;
|
||||||
|
|
||||||
/** The set of TLDs which this registrar is allowed to access. */
|
/** The set of TLDs which this registrar is allowed to access. */
|
||||||
// TODO(b/147908600): Investigate how to automatically apply user type
|
|
||||||
@org.hibernate.annotations.Type(type = "google.registry.persistence.StringSetUserType")
|
|
||||||
Set<String> allowedTlds;
|
Set<String> allowedTlds;
|
||||||
|
|
||||||
/** Host name of WHOIS server. */
|
/** Host name of WHOIS server. */
|
||||||
String whoisServer;
|
String whoisServer;
|
||||||
|
|
||||||
/** Base URLs for the registrar's RDAP servers. */
|
/** Base URLs for the registrar's RDAP servers. */
|
||||||
@org.hibernate.annotations.Type(type = "google.registry.persistence.StringSetUserType")
|
|
||||||
Set<String> rdapBaseUrls;
|
Set<String> rdapBaseUrls;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -298,7 +295,6 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
String failoverClientCertificateHash;
|
String failoverClientCertificateHash;
|
||||||
|
|
||||||
/** A whitelist of netmasks (in CIDR notation) which the client is allowed to connect from. */
|
/** A whitelist of netmasks (in CIDR notation) which the client is allowed to connect from. */
|
||||||
@org.hibernate.annotations.Type(type = "google.registry.persistence.CidrAddressBlockListUserType")
|
|
||||||
List<CidrAddressBlock> ipAddressWhitelist;
|
List<CidrAddressBlock> ipAddressWhitelist;
|
||||||
|
|
||||||
/** A hashed password for EPP access. The hash is a base64 encoded SHA256 string. */
|
/** A hashed password for EPP access. The hash is a base64 encoded SHA256 string. */
|
||||||
|
|
|
@ -42,14 +42,12 @@ import google.registry.model.ImmutableObject;
|
||||||
import google.registry.model.JsonMapBuilder;
|
import google.registry.model.JsonMapBuilder;
|
||||||
import google.registry.model.Jsonifiable;
|
import google.registry.model.Jsonifiable;
|
||||||
import google.registry.model.annotations.ReportedOn;
|
import google.registry.model.annotations.ReportedOn;
|
||||||
import google.registry.persistence.EnumSetUserType;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.Transient;
|
import javax.persistence.Transient;
|
||||||
import org.hibernate.annotations.Type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A contact for a Registrar. Note, equality, hashCode and comparable have been overridden to only
|
* A contact for a Registrar. Note, equality, hashCode and comparable have been overridden to only
|
||||||
|
@ -103,9 +101,6 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
|
||||||
this.displayName = display;
|
this.displayName = display;
|
||||||
this.required = required;
|
this.required = required;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Hibernate type for sets of {@link Type}. */
|
|
||||||
public static class RegistrarPocType extends EnumSetUserType<Type> {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The name of the contact. */
|
/** The name of the contact. */
|
||||||
|
@ -127,8 +122,6 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
|
||||||
* Multiple types are used to associate the registrar contact with various mailing groups. This
|
* Multiple types are used to associate the registrar contact with various mailing groups. This
|
||||||
* data is internal to the registry.
|
* data is internal to the registry.
|
||||||
*/
|
*/
|
||||||
@org.hibernate.annotations.Type(
|
|
||||||
type = "google.registry.model.registrar.RegistrarContact$Type$RegistrarPocType")
|
|
||||||
Set<Type> types;
|
Set<Type> types;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,20 +16,24 @@ package google.registry.persistence;
|
||||||
|
|
||||||
import google.registry.util.CidrAddressBlock;
|
import google.registry.util.CidrAddressBlock;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
import javax.persistence.Converter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hibernate {@link org.hibernate.usertype.UserType} for storing/retrieving {@link
|
* JPA {@link AttributeConverter} for storing/retrieving {@link List<CidrAddressBlock>} objects.
|
||||||
* List<CidrAddressBlock>} objects.
|
* TODO(shicong): Investigate if we can have one converter for any List type
|
||||||
*/
|
*/
|
||||||
public class CidrAddressBlockListUserType extends StringListUserType<CidrAddressBlock> {
|
@Converter(autoApply = true)
|
||||||
|
public class CidrAddressBlockListConverter extends StringListConverterBase<CidrAddressBlock> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CidrAddressBlock convertToElem(String columnValue) {
|
String toString(CidrAddressBlock element) {
|
||||||
return columnValue == null ? null : CidrAddressBlock.create(columnValue);
|
return element.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String convertToColumn(CidrAddressBlock elementValue) {
|
CidrAddressBlock fromString(String value) {
|
||||||
return elementValue == null ? null : elementValue.toString();
|
return CidrAddressBlock.create(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,51 +0,0 @@
|
||||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// 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 google.registry.persistence;
|
|
||||||
|
|
||||||
import google.registry.util.TypeUtils.TypeInstantiator;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/** Abstract Hibernate user type for storing/retrieving {@link Set<Enum<E>>}. */
|
|
||||||
public class EnumSetUserType<E extends Enum<E>>
|
|
||||||
extends GenericCollectionUserType<Set<E>, E, String> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Set<E> getNewCollection() {
|
|
||||||
return new HashSet<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
ArrayColumnType getColumnType() {
|
|
||||||
return ArrayColumnType.STRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class returnedClass() {
|
|
||||||
return Set.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected E convertToElem(String columnValue) {
|
|
||||||
return columnValue == null
|
|
||||||
? null
|
|
||||||
: Enum.valueOf(new TypeInstantiator<E>(getClass()) {}.getExactType(), columnValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String convertToColumn(E elementValue) {
|
|
||||||
return elementValue == null ? null : elementValue.toString();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,119 +0,0 @@
|
||||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// 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 google.registry.persistence;
|
|
||||||
|
|
||||||
import google.registry.util.TypeUtils.TypeInstantiator;
|
|
||||||
import java.sql.Array;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Types;
|
|
||||||
import java.util.Collection;
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic Hibernate user type to store/retrieve Java collection as an array in Cloud SQL.
|
|
||||||
*
|
|
||||||
* @param <T> the concrete {@link Collection} type of the entity field
|
|
||||||
* @param <E> the Java type of the element for the collection of the entity field
|
|
||||||
* @param <C> the JDBC supported type of the element in the DB column array
|
|
||||||
*/
|
|
||||||
public abstract class GenericCollectionUserType<T extends Collection<E>, E, C>
|
|
||||||
extends MutableUserType {
|
|
||||||
|
|
||||||
abstract T getNewCollection();
|
|
||||||
|
|
||||||
abstract ArrayColumnType getColumnType();
|
|
||||||
|
|
||||||
enum ArrayColumnType {
|
|
||||||
STRING(Types.ARRAY, "text");
|
|
||||||
|
|
||||||
final int typeCode;
|
|
||||||
final String typeName;
|
|
||||||
|
|
||||||
ArrayColumnType(int typeCode, String typeName) {
|
|
||||||
this.typeCode = typeCode;
|
|
||||||
this.typeName = typeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getTypeCode() {
|
|
||||||
return typeCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getTypeName() {
|
|
||||||
return typeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getTypeDdlName() {
|
|
||||||
return typeName + "[]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class returnedClass() {
|
|
||||||
return new TypeInstantiator<T>(getClass()) {}.getExactType();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int[] sqlTypes() {
|
|
||||||
return new int[] {getColumnType().getTypeCode()};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object nullSafeGet(
|
|
||||||
ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
|
|
||||||
throws HibernateException, SQLException {
|
|
||||||
if (rs.getArray(names[0]) != null) {
|
|
||||||
T result = getNewCollection();
|
|
||||||
for (C element : (C[]) rs.getArray(names[0]).getArray()) {
|
|
||||||
result.add(convertToElem(element));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nullSafeSet(
|
|
||||||
PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
|
|
||||||
throws HibernateException, SQLException {
|
|
||||||
if (value == null) {
|
|
||||||
st.setArray(index, null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
T collection = (T) value;
|
|
||||||
Array arr =
|
|
||||||
st.getConnection()
|
|
||||||
.createArrayOf(
|
|
||||||
getColumnType().getTypeName(),
|
|
||||||
collection.stream().map(this::convertToColumn).toArray());
|
|
||||||
st.setArray(index, arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override this to convert an element value retrieved from the database to a different type.
|
|
||||||
*
|
|
||||||
* <p>This method is useful when encoding a java type to one of the types that can be used as an
|
|
||||||
* array element.
|
|
||||||
*/
|
|
||||||
protected E convertToElem(C columnValue) {
|
|
||||||
return (E) columnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected C convertToColumn(E elementValue) {
|
|
||||||
return (C) elementValue;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,9 +13,10 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
package google.registry.persistence;
|
package google.registry.persistence;
|
||||||
|
|
||||||
import google.registry.persistence.GenericCollectionUserType.ArrayColumnType;
|
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
|
import org.hibernate.boot.model.TypeContributions;
|
||||||
import org.hibernate.dialect.PostgreSQL95Dialect;
|
import org.hibernate.dialect.PostgreSQL95Dialect;
|
||||||
|
import org.hibernate.service.ServiceRegistry;
|
||||||
|
|
||||||
/** Nomulus mapping rules for column types in Postgresql. */
|
/** Nomulus mapping rules for column types in Postgresql. */
|
||||||
public class NomulusPostgreSQLDialect extends PostgreSQL95Dialect {
|
public class NomulusPostgreSQLDialect extends PostgreSQL95Dialect {
|
||||||
|
@ -25,8 +26,15 @@ public class NomulusPostgreSQLDialect extends PostgreSQL95Dialect {
|
||||||
registerColumnType(Types.TIMESTAMP_WITH_TIMEZONE, "timestamptz");
|
registerColumnType(Types.TIMESTAMP_WITH_TIMEZONE, "timestamptz");
|
||||||
registerColumnType(Types.TIMESTAMP, "timestamptz");
|
registerColumnType(Types.TIMESTAMP, "timestamptz");
|
||||||
registerColumnType(Types.OTHER, "hstore");
|
registerColumnType(Types.OTHER, "hstore");
|
||||||
for (ArrayColumnType arrayType : ArrayColumnType.values()) {
|
registerColumnType(
|
||||||
registerColumnType(arrayType.getTypeCode(), arrayType.getTypeDdlName());
|
StringCollectionDescriptor.COLUMN_TYPE, StringCollectionDescriptor.COLUMN_DDL_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contributeTypes(
|
||||||
|
TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||||
|
super.contributeTypes(typeContributions, serviceRegistry);
|
||||||
|
typeContributions.contributeJavaTypeDescriptor(new StringCollectionDescriptor());
|
||||||
|
typeContributions.contributeSqlTypeDescriptor(new StringCollectionDescriptor());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 google.registry.persistence;
|
||||||
|
|
||||||
|
import google.registry.model.registrar.RegistrarContact.Type;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
import javax.persistence.Converter;
|
||||||
|
|
||||||
|
/** JPA {@link AttributeConverter} for storing/retrieving {@link Set<Type>}. */
|
||||||
|
@Converter(autoApply = true)
|
||||||
|
public class RegistrarPocSetConverter extends StringSetConverterBase<Type> {
|
||||||
|
@Override
|
||||||
|
String toString(Type element) {
|
||||||
|
return element.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Type fromString(String value) {
|
||||||
|
return Type.valueOf(value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 google.registry.persistence;
|
||||||
|
|
||||||
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
import javax.persistence.Converter;
|
||||||
|
|
||||||
|
/** JPA {@link AttributeConverter} for storing/retrieving {@link Set<StatusValue>}. */
|
||||||
|
@Converter(autoApply = true)
|
||||||
|
public class StatusValueSetConverter extends StringSetConverterBase<StatusValue> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String toString(StatusValue element) {
|
||||||
|
return element.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
StatusValue fromString(String value) {
|
||||||
|
return StatusValue.valueOf(value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 google.registry.persistence;
|
||||||
|
|
||||||
|
import static google.registry.persistence.StringCollectionDescriptor.StringCollection;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.sql.Array;
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.util.Collection;
|
||||||
|
import org.hibernate.type.descriptor.ValueBinder;
|
||||||
|
import org.hibernate.type.descriptor.ValueExtractor;
|
||||||
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
|
import org.hibernate.type.descriptor.java.AbstractTypeDescriptor;
|
||||||
|
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||||
|
import org.hibernate.type.descriptor.spi.JdbcRecommendedSqlTypeMappingContext;
|
||||||
|
import org.hibernate.type.descriptor.sql.BasicBinder;
|
||||||
|
import org.hibernate.type.descriptor.sql.BasicExtractor;
|
||||||
|
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link JavaTypeDescriptor} and {@link SqlTypeDescriptor} for {@link StringCollection}.
|
||||||
|
*
|
||||||
|
* <p>A {@link StringCollection} object is a simple wrapper for a {@link Collection<String>} which
|
||||||
|
* can be stored as a string array in the database. The {@link JavaTypeDescriptor} and {@link
|
||||||
|
* SqlTypeDescriptor} is used by JPA/Hibernate to map between the collection and {@link Array} which
|
||||||
|
* is the actual type that JDBC uses to read from and write to the database.
|
||||||
|
*
|
||||||
|
* @see <a
|
||||||
|
* href="https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#basic-jpa-convert">JPA
|
||||||
|
* 2.1 AttributeConverters</a>
|
||||||
|
*/
|
||||||
|
public class StringCollectionDescriptor extends AbstractTypeDescriptor<StringCollection>
|
||||||
|
implements SqlTypeDescriptor {
|
||||||
|
public static final int COLUMN_TYPE = Types.ARRAY;
|
||||||
|
public static final String COLUMN_NAME = "text";
|
||||||
|
public static final String COLUMN_DDL_NAME = COLUMN_NAME + "[]";
|
||||||
|
|
||||||
|
protected StringCollectionDescriptor() {
|
||||||
|
super(StringCollection.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringCollection fromString(String string) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Constructing StringCollectionDescriptor from string is not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> X unwrap(StringCollection value, Class<X> type, WrapperOptions options) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (Collection.class.isAssignableFrom(type)) {
|
||||||
|
return (X) value.getCollection();
|
||||||
|
}
|
||||||
|
throw unknownUnwrap(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> StringCollection wrap(X value, WrapperOptions options) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (value instanceof Array) {
|
||||||
|
try {
|
||||||
|
String[] arr = (String[]) ((Array) value).getArray();
|
||||||
|
ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
|
||||||
|
for (String str : arr) {
|
||||||
|
builder.add(str);
|
||||||
|
}
|
||||||
|
return StringCollection.create(builder.build());
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw unknownWrap(value.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlTypeDescriptor getJdbcRecommendedSqlType(JdbcRecommendedSqlTypeMappingContext context) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSqlType() {
|
||||||
|
return COLUMN_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeRemapped() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
|
||||||
|
return new BasicBinder<X>(javaTypeDescriptor, this) {
|
||||||
|
@Override
|
||||||
|
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
if (value == null) {
|
||||||
|
st.setArray(index, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value instanceof StringCollection) {
|
||||||
|
StringCollection stringCollection = (StringCollection) value;
|
||||||
|
if (stringCollection.getCollection() == null) {
|
||||||
|
st.setArray(index, null);
|
||||||
|
} else {
|
||||||
|
st.setArray(
|
||||||
|
index,
|
||||||
|
st.getConnection()
|
||||||
|
.createArrayOf(COLUMN_NAME, stringCollection.getCollection().toArray()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
String.format(
|
||||||
|
"Binding type %s is not supported by StringCollectionDescriptor",
|
||||||
|
value.getClass().getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
// CallableStatement.setArray() doesn't have an overload version for setting array by its
|
||||||
|
// column name
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Binding array by its column name is not supported");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
|
||||||
|
return new BasicExtractor<X>(javaTypeDescriptor, this) {
|
||||||
|
@Override
|
||||||
|
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
|
||||||
|
return javaTypeDescriptor.wrap(rs.getArray(name), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, int index, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
return javaTypeDescriptor.wrap(statement.getArray(index), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
return javaTypeDescriptor.wrap(statement.getArray(name), options);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class StringCollection {
|
||||||
|
private Collection<String> collection;
|
||||||
|
|
||||||
|
private StringCollection(Collection<String> collection) {
|
||||||
|
this.collection = collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StringCollection create(Collection<String> collection) {
|
||||||
|
return new StringCollection(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<String> getCollection() {
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,19 +14,21 @@
|
||||||
|
|
||||||
package google.registry.persistence;
|
package google.registry.persistence;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
import javax.persistence.Converter;
|
||||||
|
|
||||||
/** Abstract Hibernate user type for storing/retrieving {@link List<String>}. */
|
/** JPA {@link AttributeConverter} for storing/retrieving {@link List<String>}. */
|
||||||
public class StringListUserType<E> extends GenericCollectionUserType<List<E>, E, String> {
|
@Converter(autoApply = true)
|
||||||
|
public class StringListConverter extends StringListConverterBase<String> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
List<E> getNewCollection() {
|
String toString(String element) {
|
||||||
return Lists.newArrayList();
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ArrayColumnType getColumnType() {
|
String fromString(String value) {
|
||||||
return ArrayColumnType.STRING;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 google.registry.persistence;
|
||||||
|
|
||||||
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||||
|
|
||||||
|
import google.registry.persistence.StringCollectionDescriptor.StringCollection;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base JPA converter for {@link List} objects that are stored as an array of strings in the
|
||||||
|
* database.
|
||||||
|
*/
|
||||||
|
public abstract class StringListConverterBase<T>
|
||||||
|
implements AttributeConverter<List<T>, StringCollection> {
|
||||||
|
|
||||||
|
abstract String toString(T element);
|
||||||
|
|
||||||
|
abstract T fromString(String value);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringCollection convertToDatabaseColumn(List<T> attribute) {
|
||||||
|
return attribute == null
|
||||||
|
? null
|
||||||
|
: StringCollection.create(
|
||||||
|
attribute.stream().map(this::toString).collect(toImmutableList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<T> convertToEntityAttribute(StringCollection dbData) {
|
||||||
|
return dbData == null || dbData.getCollection() == null
|
||||||
|
? null
|
||||||
|
: dbData.getCollection().stream().map(this::fromString).collect(toImmutableList());
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,19 +14,21 @@
|
||||||
|
|
||||||
package google.registry.persistence;
|
package google.registry.persistence;
|
||||||
|
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
import javax.persistence.Converter;
|
||||||
|
|
||||||
/** Abstract Hibernate user type for storing/retrieving {@link Set<String>}. */
|
/** JPA {@link AttributeConverter} for storing/retrieving {@link Set<String>}. */
|
||||||
public class StringSetUserType<E> extends GenericCollectionUserType<Set<E>, E, String> {
|
@Converter(autoApply = true)
|
||||||
|
public class StringSetConverter extends StringSetConverterBase<String> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Set<E> getNewCollection() {
|
String toString(String element) {
|
||||||
return Sets.newHashSet();
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ArrayColumnType getColumnType() {
|
String fromString(String value) {
|
||||||
return ArrayColumnType.STRING;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 google.registry.persistence;
|
||||||
|
|
||||||
|
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||||
|
|
||||||
|
import google.registry.persistence.StringCollectionDescriptor.StringCollection;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base JPA converter for {@link Set} objects that are stored as an array of strings in the
|
||||||
|
* database.
|
||||||
|
*/
|
||||||
|
public abstract class StringSetConverterBase<T>
|
||||||
|
implements AttributeConverter<Set<T>, StringCollection> {
|
||||||
|
|
||||||
|
abstract String toString(T element);
|
||||||
|
|
||||||
|
abstract T fromString(String value);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringCollection convertToDatabaseColumn(Set<T> attribute) {
|
||||||
|
return attribute == null
|
||||||
|
? null
|
||||||
|
: StringCollection.create(attribute.stream().map(this::toString).collect(toImmutableSet()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<T> convertToEntityAttribute(StringCollection dbData) {
|
||||||
|
return dbData == null || dbData.getCollection() == null
|
||||||
|
? null
|
||||||
|
: dbData.getCollection().stream().map(this::fromString).collect(toImmutableSet());
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,9 +39,14 @@
|
||||||
|
|
||||||
<!-- Customized type converters -->
|
<!-- Customized type converters -->
|
||||||
<class>google.registry.persistence.BloomFilterConverter</class>
|
<class>google.registry.persistence.BloomFilterConverter</class>
|
||||||
|
<class>google.registry.persistence.CidrAddressBlockListConverter</class>
|
||||||
<class>google.registry.persistence.CreateAutoTimestampConverter</class>
|
<class>google.registry.persistence.CreateAutoTimestampConverter</class>
|
||||||
<class>google.registry.persistence.CurrencyUnitConverter</class>
|
<class>google.registry.persistence.CurrencyUnitConverter</class>
|
||||||
<class>google.registry.persistence.DateTimeConverter</class>
|
<class>google.registry.persistence.DateTimeConverter</class>
|
||||||
|
<class>google.registry.persistence.RegistrarPocSetConverter</class>
|
||||||
|
<class>google.registry.persistence.StatusValueSetConverter</class>
|
||||||
|
<class>google.registry.persistence.StringListConverter</class>
|
||||||
|
<class>google.registry.persistence.StringSetConverter</class>
|
||||||
<class>google.registry.persistence.UpdateAutoTimestampConverter</class>
|
<class>google.registry.persistence.UpdateAutoTimestampConverter</class>
|
||||||
<class>google.registry.persistence.ZonedDateTimeConverter</class>
|
<class>google.registry.persistence.ZonedDateTimeConverter</class>
|
||||||
|
|
||||||
|
|
|
@ -25,13 +25,12 @@ import google.registry.util.CidrAddressBlock;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import org.hibernate.annotations.Type;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
/** Unit tests for {@link CidrAddressBlockListUserType}. */
|
/** Unit tests for {@link CidrAddressBlockListConverter}. */
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class CidrAddressBlockListUserTypeTest {
|
public class CidrAddressBlockListUserTypeTest {
|
||||||
@Rule
|
@Rule
|
||||||
|
@ -59,7 +58,6 @@ public class CidrAddressBlockListUserTypeTest {
|
||||||
|
|
||||||
@Id String name = "id";
|
@Id String name = "id";
|
||||||
|
|
||||||
@Type(type = "google.registry.persistence.CidrAddressBlockListUserType")
|
|
||||||
List<CidrAddressBlock> addresses;
|
List<CidrAddressBlock> addresses;
|
||||||
|
|
||||||
private TestEntity() {}
|
private TestEntity() {}
|
||||||
|
|
|
@ -18,29 +18,27 @@ import static com.google.common.truth.Truth.assertThat;
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
import google.registry.persistence.transaction.JpaTestRules;
|
import google.registry.persistence.transaction.JpaTestRules;
|
||||||
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
|
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import org.hibernate.annotations.Type;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
/** Unit tests for {@link EnumSetUserType}. */
|
/** Unit tests for {@link StatusValueSetConverter}. */
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class EnumSetUserTypeTest {
|
public class StatusValueSetConverterTest {
|
||||||
@Rule
|
@Rule
|
||||||
public final JpaUnitTestRule jpaRule =
|
public final JpaUnitTestRule jpaRule =
|
||||||
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
|
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
|
||||||
|
|
||||||
public EnumSetUserTypeTest() {}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRoundTrip() {
|
public void testRoundTrip() {
|
||||||
Set<TestEnum> enums = ImmutableSet.of(TestEnum.BAR, TestEnum.FOO);
|
Set<StatusValue> enums = ImmutableSet.of(StatusValue.INACTIVE, StatusValue.PENDING_DELETE);
|
||||||
TestEntity obj = new TestEntity("foo", enums);
|
TestEntity obj = new TestEntity("foo", enums);
|
||||||
|
|
||||||
jpaTm().transact(() -> jpaTm().getEntityManager().persist(obj));
|
jpaTm().transact(() -> jpaTm().getEntityManager().persist(obj));
|
||||||
|
@ -49,45 +47,15 @@ public class EnumSetUserTypeTest {
|
||||||
assertThat(persisted.data).isEqualTo(enums);
|
assertThat(persisted.data).isEqualTo(enums);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNativeQuery_succeeds() {
|
|
||||||
Set<TestEnum> enums = ImmutableSet.of(TestEnum.BAR, TestEnum.FOO);
|
|
||||||
TestEntity obj = new TestEntity("foo", enums);
|
|
||||||
|
|
||||||
jpaTm().transact(() -> jpaTm().getEntityManager().persist(obj));
|
|
||||||
|
|
||||||
assertThat(
|
|
||||||
ImmutableSet.of(
|
|
||||||
getSingleResultFromNativeQuery(
|
|
||||||
"SELECT data[1] FROM \"TestEntity\" WHERE name = 'foo'"),
|
|
||||||
getSingleResultFromNativeQuery(
|
|
||||||
"SELECT data[2] FROM \"TestEntity\" WHERE name = 'foo'")))
|
|
||||||
.containsExactly("BAR", "FOO");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object getSingleResultFromNativeQuery(String sql) {
|
|
||||||
return jpaTm()
|
|
||||||
.transact(() -> jpaTm().getEntityManager().createNativeQuery(sql).getSingleResult());
|
|
||||||
}
|
|
||||||
|
|
||||||
enum TestEnum {
|
|
||||||
FOO,
|
|
||||||
BAR,
|
|
||||||
BAZ;
|
|
||||||
|
|
||||||
public static class TestEnumType extends EnumSetUserType<TestEnum> {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entity(name = "TestEntity")
|
@Entity(name = "TestEntity")
|
||||||
static class TestEntity {
|
static class TestEntity {
|
||||||
@Id String name;
|
@Id String name;
|
||||||
|
|
||||||
@Type(type = "google.registry.persistence.EnumSetUserTypeTest$TestEnum$TestEnumType")
|
Set<StatusValue> data;
|
||||||
Set<TestEnum> data;
|
|
||||||
|
|
||||||
TestEntity() {}
|
TestEntity() {}
|
||||||
|
|
||||||
TestEntity(String name, Set<TestEnum> data) {
|
TestEntity(String name, Set<StatusValue> data) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
|
@ -26,15 +26,14 @@ import java.util.List;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.NoResultException;
|
import javax.persistence.NoResultException;
|
||||||
import org.hibernate.annotations.Type;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
/** Unit tests for {@link StringListUserType}. */
|
/** Unit tests for {@link StringListConverter}. */
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class StringListUserTypeTest {
|
public class StringListConverterTest {
|
||||||
@Rule
|
@Rule
|
||||||
public final JpaUnitTestRule jpaRule =
|
public final JpaUnitTestRule jpaRule =
|
||||||
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
|
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
|
||||||
|
@ -123,7 +122,6 @@ public class StringListUserTypeTest {
|
||||||
|
|
||||||
@Id String name = "id";
|
@Id String name = "id";
|
||||||
|
|
||||||
@Type(type = "google.registry.persistence.StringListUserType")
|
|
||||||
List<String> tlds;
|
List<String> tlds;
|
||||||
|
|
||||||
private TestEntity() {}
|
private TestEntity() {}
|
|
@ -24,15 +24,14 @@ import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import org.hibernate.annotations.Type;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
/** Unit tests for {@link StringSetUserType}. */
|
/** Unit tests for {@link StringSetConverter}. */
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class StringSetUserTypeTest {
|
public class StringSetConverterTest {
|
||||||
@Rule
|
@Rule
|
||||||
public final JpaUnitTestRule jpaRule =
|
public final JpaUnitTestRule jpaRule =
|
||||||
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
|
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
|
||||||
|
@ -70,7 +69,6 @@ public class StringSetUserTypeTest {
|
||||||
|
|
||||||
@Id String name = "id";
|
@Id String name = "id";
|
||||||
|
|
||||||
@Type(type = "google.registry.persistence.StringSetUserType")
|
|
||||||
Set<String> tlds;
|
Set<String> tlds;
|
||||||
|
|
||||||
private TestEntity() {}
|
private TestEntity() {}
|
Loading…
Add table
Add a link
Reference in a new issue