Configure hibernate (using JPA) to store Y/N for type Boolean instead of 0/1

JavaHibernateJpa

Java Problem Overview


Can I setup JPA/hibernate to persist Boolean types as Y/N? In the database (the column is defined as varchar2(1). It currently stores them as 0/1. The database is Oracle.

Java Solutions


Solution 1 - Java

Hibernate has a built-in "yes_no" type that would do what you want. It maps to a CHAR(1) column in the database.

Basic mapping: <property name="some_flag" type="yes_no"/>

Annotation mapping (Hibernate extensions):

@Type(type="yes_no")
public boolean getFlag();

Solution 2 - Java

This is pure JPA without using getters/setters. As of 2013/2014 it is the best answer without using any Hibernate specific annotations, but please note this solution is JPA 2.1, and was not available when the question was first asked:

@Entity
public class Person {    

    @Convert(converter=BooleanToStringConverter.class)
    private Boolean isAlive;    
    ...
}

And then:

@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);
        }
    }

Edit:

The implementation above considers anything different from character "Y", including null, as false. Is that correct? Some people here consider this incorrect, and believe that null in the database should be null in Java.

But if you return null in Java, it will give you a NullPointerException if your field is a primitive boolean. In other words, unless some of your fields actually use the class Boolean it's best to consider null as false, and use the above implementation. Then Hibernate will not to emit any exceptions regardless of the contents of the database.

And if you do want to accept null and emit exceptions if the contents of the database are not strictly correct, then I guess you should not accept any characters apart from "Y", "N" and null. Make it consistent, and don't accept any variations like "y", "n", "0" and "1", which will only make your life harder later. This is a more strict implementation:

@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);
    }

And yet another option, if you want to allow for null in Java but not in the database:

@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);
    }

Solution 3 - Java

I used the concept from the answer posted by @marcg and it works great with JPA 2.1. His code wasn't quite right, so I'm posted my working implementation. This will convert Boolean entity fields to a Y/N character column in the database.

From my entity class:

@Convert(converter=BooleanToYNStringConverter.class)
@Column(name="LOADED", length=1)
private Boolean isLoadedSuccessfully;

My converter class:

/**
 * 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;
	}

}

This variant is also fun if you love emoticons and are just sick and tired of Y/N or T/F in your database. In this case, your database column must be two characters instead of one. Probably not a big deal.

/**
 * 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;
	}

}

Solution 4 - Java

The only way I've figured out how to do this is to have two properties for my class. One as the boolean for the programming API which is not included in the mapping. It's getter and setter reference a private char variable which is Y/N. I then have another protected property which is included in the hibernate mapping and it's getters and setters reference the private char variable directly.

EDIT: As has been pointed out there are other solutions that are directly built into Hibernate. I'm leaving this answer because it can work in situations where you're working with a legacy field that doesn't play nice with the built in options. On top of that there are no serious negative consequences to this approach.

Solution 5 - Java

To even do better boolean mapping to Y/N, add to your hibernate configuration:

<!-- 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>

Now you can use booleans in HQL, for example:

"FROM " + SomeDomainClass.class.getName() + " somedomainclass " +
"WHERE somedomainclass.someboolean = false"

Solution 6 - Java

To do it in a generic JPA way using getter annotations, the example below works for me with Hibernate 3.5.4 and Oracle 11g. Note that the mapped getter and setter (getOpenedYnString and setOpenedYnString) are private methods. Those methods provide the mapping but all programmatic access to the class is using the getOpenedYn and setOpenedYn methods.

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;
}

Here's the util class with static methods toYesNo and 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);
	}
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsengsView Question on Stackoverflow
Solution 1 - JavaChssPly76View Answer on Stackoverflow
Solution 2 - JavaMarcGView Answer on Stackoverflow
Solution 3 - JavaJim ToughView Answer on Stackoverflow
Solution 4 - JavaSpencer RuportView Answer on Stackoverflow
Solution 5 - JavaM.A. HogendoornView Answer on Stackoverflow
Solution 6 - JavaDave MotenView Answer on Stackoverflow