Исключение JDBC java.sql.SQLException при вставке преобразованного значения перечисления в столбец MySQL Smallint

#mysql #spring #spring-jdbc #enumeration

#mysql #spring #spring-jdbc #перечисление

Вопрос:

Я пытаюсь использовать перечисление для отображения удобочитаемых значений для поля «разрешение» (ЧТЕНИЕ, ЗАПИСЬ и т. Д.). Когда я сохраняю запись в связанной таблице MySQL, поле перечисления разрешений необходимо преобразовать в соответствующее целое число (1, 2, 4 и т.д.). Однако, когда я запускаю приложение SpringBoot и вызываю конечную точку POST, я получаю странное исключение SQL. Вот исключение:

Не удалось выполнить DbAction.InsertRoot(entity=Invite(id= null, createdAt= 2020-09-19 18:12:01, CreatedBy=1, ListID= 8, token= c3b95dc9-ec4d-4dde-a461-b12746afa61e, expiresAt= 2020-09-26 18:12:01, permission = 2), generatedId= null)] с корнемпричина java.sql.SQLException: неверное целочисленное значение: «ЗАПИСЬ» для столбца «разрешение» в строке 1 в com.mysql.cj.jdbc.exceptions.Исключение SqlError.createSQLException(SqlError.java:129) ~ [mysql-connector-java-8.0.19.jar:8.0.19]

Мне кажется странным, что действие db сообщит, что поле «разрешение» для вставки равно 2, но исключение SQL сообщает, что «ЗАПИСЬ» не является допустимым целочисленным значением. Есть идеи, чего мне может не хватать?

Вот примечательный код и конфигурация. Пожалуйста, обратите внимание, что я использую JDBC, а не JPA:

Вот класс enum:

 public enum Permission {
    READ(1), WRITE(2), CREATE(4), DELETE(8), ADMIN(16);

    private Integer value;

    Permission(final Integer value) {
        this.value = value;
    }

    public Integer getValue() {
        return this.value;
    }

    //@JsonCreator
    @Override
    public String toString() {
        return this.getValue().toString();
    }

    //@JsonValue
    public static Permission forValue(final String name) {
        return Permission.valueOf(name);
    }
}
  

Здесь POJO / Entity:

 import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
import org.springframework.data.annotation.Id;

@Data
@AllArgsConstructor
@Builder
@ToString
public class Invite {

    public static final int TOKEN_MINIMUM_LENGTH = 36;
    public static final int TOKEN_MAXIMUM_LENGTH = 40;

    @Id
    private Long id;

    private String createdAt;

    private String createdBy;

    private Long listId;

    private String token;

    private String expiresAt;

    private Permission permission;

}
  

Вот JdbcCustomConversions . Следует отметить, что ReadingConverter, похоже, отключается при отладке, а WritingConverter — нет. Я не уверен, является ли это частью проблемы или нет.

 
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration;
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
import java.util.Arrays;

@Configuration
@EnableAutoConfiguration
public class JdbcConfiguration extends AbstractJdbcConfiguration {

    /**
     * JDBC Custom Conversions.
     *
     */
    @Override
    public JdbcCustomConversions jdbcCustomConversions() {
        return new JdbcCustomConversions(Arrays.asList(PermissionToInteger.INSTANCE, IntegerToPermission.INSTANCE));
    }

    // I have never seen this get tripped
    @WritingConverter
    enum PermissionToInteger implements Converter<Permission, Integer> {

        INSTANCE;

        @Override
        public Integer convert(final Permission permission) {
            return permission == null ? null : permission.getValue();
        }
    }

    // this one trips in the debugger and appears to be working fine
    @ReadingConverter
    enum IntegerToPermission implements Converter<Integer, Permission> {

        INSTANCE;

        @Override
        public Permission convert(final Integer code) {
            for (final Permission perm: Permission.values()) {
                if (perm.getValue().equals(code)) {
                    return perm;
                }
            }
            return null;
        }
    }
}
  

Вот простой репозиторий:

 import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
import java.util.Optional;

public interface InviteRepository extends CrudRepository<Invite, Long> {

    @Query("SELECT * from invite where token = :token")
    Optional<Invite> findByToken(String token);

    @Query("SELECT * from invite where created_by = :userId")
    List<Invite> findByUserId(Long userId);

    @Query("SELECT * from invite where list_id = :wishlistId")
    List<Invite> findByWishlistId(Long wishlistId);

}
  

Любые советы будут с благодарностью!

Аарон

Комментарии:

1. Вам нужно использовать @Enumerated аннотацию. Здесь вы можете найти статью об этом: Как сопоставить перечисление со столбцом базы данных, а также с этим: baeldung.com/jpa-persisting-enums-in-jpa

Ответ №1:

Исправление состояло в том, чтобы изменить WritingConverter на String вместо Integer:

 @WritingConverter
public enum PermissionToInteger implements Converter<Permission, String> {

    INSTANCE;

    @Override
    public String convert(final Permission permission) {
        return permission == null ? null : permission.getValue().toString();
    }
}
  

Ответ №2:

После некоторой отладки исходного кода spring jdbc он может преобразовать тип перечисления в целое число по назначению целевого класса из класса JdbcValue.

    @Bean
   override fun jdbcCustomConversions() = JdbcCustomConversions(arrayListOf(
        @WritingConverter
        object: Converter<LoginTypes, JdbcValue> {
            override fun convert(p0: LoginTypes) = JdbcValue.of(p0.code,JDBCType.INTEGER)
        },
        @ReadingConverter
        object:Converter<Int,LoginTypes>{
            override fun convert(p0: Int): LoginTypes? = LoginTypes.values().first { it.code==p0 }
        }```