6
votes

When using the Hibernate validator to verify that JPA entities match the database schema (typically be setting hibernate.ddl-auto = verify) it will fail when it encounters a MySQL CHAR that has been mapped to a String field.

For example, this MySQL table schema:

CREATE TABLE `item` (
  `id` bigint(20) NOT NULL,
  `name` char(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

May be mapped to a JPA entity like this:

@Entity
public class Item
{
    @Id
    private Long id;

    @Column
    private String name;
}

The verification process will fail with the following exception:

org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: wrong column type encountered in column [name] in table [item]; found [char (Types#CHAR)], but expecting [varchar(255) (Types#VARCHAR)]

A typical solution to this is to use the Hibernate @Type annotation on the field to specify the column data type, this works well enough to satisfy the validator:

@Column
@Type(type = "char")
private String name;

When you actually issue a query that requires mapping a ResultSet to the entity, you will encounter a different set of exceptions (in this case Spring Data was used to issue the query):

org.springframework.orm.jpa.JpaSystemException: Could not set field value [B] value by reflection : [class com.example.Item.name] setter of com.example.Item.name; nested exception is org.hibernate.PropertyAccessException: Could not set field value [B] value by reflection : [class com.example.Item.name] setter of com.example.Item.name

and

java.lang.IllegalArgumentException: Can not set java.lang.String field com.example.Item.name to java.lang.Character

1
Do not use char type in MySQL... Most of the time it is mapped to varchar anyway, but confuses tools like JPA/Hibernate... - Usagi Miyamoto
Also a column of char(50) CHARACTER SET utf8mb4 is using 200 bytes per row, as "utf8mb4" takes up to 4 bytes per chars... See: dev.mysql.com/doc/refman/5.7/en/charset-unicode-utf8mb4.html - Usagi Miyamoto
@UsagiMiyamoto That's great in an ideal world but sometimes you're working with a legacy schema that you have no control over which uses char so you need to compromise. - Robert Hunt

1 Answers

15
votes

The solution to these exceptions is to set the column definition in the actual JPA @Column annotation rather than the Hibernate specific @Type annotation:

@Column(columnDefinition = "char")
private String name;

This will satisfy the Hibernate validator without confusing Hibernate into thinking you're actually mapping a Character field.