java - una - Configure hibernate(usando JPA) para almacenar Y/N para tipo Boolean en lugar de 0/1
mapear tablas hibernate (7)
El uso de convertidores JPA 2.1 es la mejor solución, sin embargo, si está utilizando la versión anterior de JPA, puedo recomendar una solución más (o solución alternativa). Cree una enumeración llamada BooleanWrapper con 2 valores de T y F y añádale el siguiente método para obtener el valor ajustado: public Boolean getValue() { return this == T; }
public Boolean getValue() { return this == T; }
, asócielo con @Enumerated (EnumType.STRING).
¿Puedo configurar JPA / hibernate para que persista en Boolean
tipos Boolean
como Y/N
? En la base de datos (la columna se define como varchar2(1)
. Actualmente los almacena como 0/1
. La base de datos es Oracle.
Esto es JPA puro sin usar getters / setters. A partir de 2013/2014 es la mejor respuesta sin utilizar ninguna anotación específica de Hibernate, pero tenga en cuenta que esta solución es JPA 2.1 y no estaba disponible cuando se hizo la pregunta por primera vez:
@Entity
public class Person {
@Convert(converter=BooleanToStringConverter.class)
private Boolean isAlive;
...
}
Y entonces:
@Converter
public class BooleanToStringConverter implements AttributeConverter<Boolean, String> {
@Override
public String convertToDatabaseColumn(Boolean value) {
return (value != null && value) ? "Y" : "N";
}
@Override
public Boolean convertToEntityAttribute(String value) {
return "Y".equals(value);
}
}
Editar:
La implementación anterior considera cualquier cosa diferente del carácter "Y", incluido null
, como false
. ¿Es eso correcto? Algunas personas aquí consideran que esto es incorrecto, y creen que la null
en la base de datos debe ser null
en Java.
Pero si devuelve null
en Java, le dará una NullPointerException
si su campo es un booleano primitivo . En otras palabras, a menos que algunos de sus campos usen realmente la clase Boolean , es mejor considerar null
como false
y usar la implementación anterior. Entonces Hibernate no emitirá ninguna excepción independientemente del contenido de la base de datos.
Y si desea aceptar excepciones null
y emitir excepciones si el contenido de la base de datos no es estrictamente correcto, entonces supongo que no debe aceptar ningún carácter aparte de "Y", "N" y null
. Consígalo coherente y no acepte ninguna variación como "y", "n", "0" y "1", lo que hará que su vida sea más difícil más adelante. Esta es una implementación más estricta:
@Override
public String convertToDatabaseColumn(Boolean value) {
if (value == null) return null;
else return value ? "Y" : "N";
}
@Override
public Boolean convertToEntityAttribute(String value) {
if (value == null) return null;
else if (value.equals("Y")) return true;
else if (value.equals("N")) return false;
else throw new IllegalStateException("Invalid boolean character: " + value);
}
Y aún otra opción, si desea permitir null
en Java pero no en la base de datos:
@Override
public String convertToDatabaseColumn(Boolean value) {
if (value == null) return "-";
else return value ? "Y" : "N";
}
@Override
public Boolean convertToEntityAttribute(String value) {
if (value.equals("-") return null;
else if (value.equals("Y")) return true;
else if (value.equals("N")) return false;
else throw new IllegalStateException("Invalid boolean character: " + value);
}
Hibernate tiene incorporado un tipo "yes_no" que haría lo que usted desea. Se asigna a una columna CHAR (1) en la base de datos.
Mapeo básico: <property name="some_flag" type="yes_no"/>
Mapeo de anotaciones (extensiones de Hibernate):
@Type(type="yes_no")
public boolean getFlag();
La única forma en que he descubierto cómo hacerlo es tener dos propiedades para mi clase. Uno como booleano para la API de programación que no está incluida en la asignación. Es getter y setter reference una variable char privada que es Y / N. Luego tengo otra propiedad protegida que está incluida en el mapeo de hibernación y sus captadores y definidores hacen referencia a la variable de char privado directamente.
Para hacerlo de una manera genérica JPA utilizando anotaciones getter, el ejemplo siguiente funciona para mí con Hibernate 3.5.4 y Oracle 11g. Tenga en cuenta que el getter y setter getOpenedYnString
( getOpenedYnString
y setOpenedYnString
) son métodos privados. Esos métodos proporcionan el mapeo pero todo el acceso programático a la clase está usando los métodos getOpenedYn
y setOpenedYn
.
private String openedYn;
@Transient
public Boolean getOpenedYn() {
return toBoolean(openedYn);
}
public void setOpenedYn(Boolean openedYn) {
setOpenedYnString(toYesNo(openedYn));
}
@Column(name = "OPENED_YN", length = 1)
private String getOpenedYnString() {
return openedYn;
}
private void setOpenedYnString(String openedYn) {
this.openedYn = openedYn;
}
Aquí está la clase util con métodos estáticos toYesNo
y toBoolean
:
public class JpaUtil {
private static final String NO = "N";
private static final String YES = "Y";
public static String toYesNo(Boolean value) {
if (value == null)
return null;
else if (value)
return YES;
else
return NO;
}
public static Boolean toBoolean(String yesNo) {
if (yesNo == null)
return null;
else if (YES.equals(yesNo))
return true;
else if (NO.equals(yesNo))
return false;
else
throw new RuntimeException("unexpected yes/no value:" + yesNo);
}
}
Para incluso hacer una mejor asignación booleana a Y / N, agregue a su configuración de hibernación:
<!-- when using type="yes_no" for booleans, the line below allow booleans in HQL expressions: -->
<property name="hibernate.query.substitutions">true ''Y'', false ''N''</property>
Ahora puede usar booleanos en HQL, por ejemplo:
"FROM " + SomeDomainClass.class.getName() + " somedomainclass " +
"WHERE somedomainclass.someboolean = false"
Usé el concepto de la respuesta publicada por @marcg y funciona muy bien con JPA 2.1. Su código no era del todo correcto, así que publiqué mi implementación en funcionamiento. Esto convertirá Boolean
campos de entidad Boolean
en una columna de caracteres Y / N en la base de datos.
De mi clase de entidad:
@Convert(converter=BooleanToYNStringConverter.class)
@Column(name="LOADED", length=1)
private Boolean isLoadedSuccessfully;
Mi clase de convertidor:
/**
* Converts a Boolean entity attribute to a single-character
* Y/N string that will be stored in the database, and vice-versa
*
* @author jtough
*/
public class BooleanToYNStringConverter
implements AttributeConverter<Boolean, String> {
/**
* This implementation will return "Y" if the parameter is Boolean.TRUE,
* otherwise it will return "N" when the parameter is Boolean.FALSE.
* A null input value will yield a null return value.
* @param b Boolean
*/
@Override
public String convertToDatabaseColumn(Boolean b) {
if (b == null) {
return null;
}
if (b.booleanValue()) {
return "Y";
}
return "N";
}
/**
* This implementation will return Boolean.TRUE if the string
* is "Y" or "y", otherwise it will ignore the value and return
* Boolean.FALSE (it does not actually look for "N") for any
* other non-null string. A null input value will yield a null
* return value.
* @param s String
*/
@Override
public Boolean convertToEntityAttribute(String s) {
if (s == null) {
return null;
}
if (s.equals("Y") || s.equals("y")) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
}
Esta variante también es divertida si te gustan los emoticones y estás cansada de Y / N o T / F en tu base de datos. En este caso, su columna de base de datos debe tener dos caracteres en lugar de uno. Probablemente no es un gran problema.
/**
* Converts a Boolean entity attribute to a happy face or sad face
* that will be stored in the database, and vice-versa
*
* @author jtough
*/
public class BooleanToHappySadConverter
implements AttributeConverter<Boolean, String> {
public static final String HAPPY = ":)";
public static final String SAD = ":(";
/**
* This implementation will return ":)" if the parameter is Boolean.TRUE,
* otherwise it will return ":(" when the parameter is Boolean.FALSE.
* A null input value will yield a null return value.
* @param b Boolean
* @return String or null
*/
@Override
public String convertToDatabaseColumn(Boolean b) {
if (b == null) {
return null;
}
if (b) {
return HAPPY;
}
return SAD;
}
/**
* This implementation will return Boolean.TRUE if the string
* is ":)", otherwise it will ignore the value and return
* Boolean.FALSE (it does not actually look for ":(") for any
* other non-null string. A null input value will yield a null
* return value.
* @param s String
* @return Boolean or null
*/
@Override
public Boolean convertToEntityAttribute(String s) {
if (s == null) {
return null;
}
if (HAPPY.equals(s)) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
}