@Converter аннотированный класс, который не распознается автоматически jpa

@Converter(introduced since JPA2.1) представляется очень удобным способом переключения значений базы данных на значение, которое мы хотим видеть в нашем Java-коде. Однако я не могу заставить эту работу работать. JPA, похоже, не знает о конвертере, который я написал, и сам пытается преобразовать значения в соответствующий класс.

Это код, который я написал:

@Converter(autoApply = true)
public class DateConverter implements AttributeConverter<Date, String> {
    @Override
    public String convertToDatabaseColumn(Date b) {
        System.out.println("Converting to Database Columns");
        return "";
    }

    @Override
    public Date convertToEntityAttribute(String string) {
        System.out.println("Converting to Entity Attribute");
        return new Date();
    }
}

Соответствующая часть, написанная в модели:

@Entity
@Table(name = "test_table")
public class TestClass extends BaseModel {
    private static final long serialVersionUID = 1L;

    @Id
    private Integer           id;

    @Convert(converter = DateConverter.class)
    @Column(name = "is_dominant_project_unit_type")
    private Date              isDominantProjectUnitType;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Date getIsDominantProjectUnitType() {
        return isDominantProjectUnitType;
    }

    public void setIsDominantProjectUnitType(Date isDominantProjectUnitType) {
        this.isDominantProjectUnitType = isDominantProjectUnitType;
    }
}

При выполнении запроса я получаю следующую ошибку:

com.abcd.exception.ProAPIException: java.util.concurrent.ExecutionException: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not execute query
    at com.abcd.data.service.trend.TrendService.getPaginatedTrend(TrendService.java:111) ~[classes/:na]
    at com.abcd.data.service.trend.TrendService.getHithertoPaginatedTrend(TrendService.java:167) ~[classes/:na]
    at com.abcd.data.mvc.trend.TrendController.getHithertoTrend(TrendController.java:183) ~[classes/:na]
    at com.abcd.data.mvc.trend.TrendController$$FastClassByCGLIB$$44adcd0f.invoke(<generated>) ~[spring-core-3.2.2.RELEASE.jar:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698) ~[spring-aop-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) ~[spring-aop-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91) ~[spring-aop-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) ~[spring-aop-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631) ~[spring-aop-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at com.abcd.data.mvc.trend.TrendController$$EnhancerByCGLIB$$faeff680.getHithertoTrend(<generated>) ~[spring-core-3.2.2.RELEASE.jar:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_55]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_55]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_55]
    at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_55]
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219) ~[spring-web-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132) ~[spring-web-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) ~[spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745) ~[spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686) ~[spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80) ~[spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925) ~[spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856) ~[spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936) [spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:827) [spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812) [spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449) [shiro-web-1.2.1.jar:1.2.1]
    at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365) [shiro-web-1.2.1.jar:1.2.1]
    at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90) [shiro-core-1.2.1.jar:1.2.1]
    at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83) [shiro-core-1.2.1.jar:1.2.1]
    at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383) [shiro-core-1.2.1.jar:1.2.1]
    at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362) [shiro-web-1.2.1.jar:1.2.1]
    at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) [shiro-web-1.2.1.jar:1.2.1]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310) [tomcat-embed-core-7.0.37.jar:7.0.37]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [na:1.7.0_55]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [na:1.7.0_55]
    at java.lang.Thread.run(Thread.java:745) [na:1.7.0_55]
Caused by: java.util.concurrent.ExecutionException: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not execute query
    at java.util.concurrent.FutureTask.report(FutureTask.java:122) ~[na:1.7.0_55]
    at java.util.concurrent.FutureTask.get(FutureTask.java:188) ~[na:1.7.0_55]
    at com.abcd.data.service.trend.TrendService.getPaginatedTrend(TrendService.java:100) ~[classes/:na]
    ... 52 common frames omitted
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not execute query
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1387) ~[hibernate-entitymanager-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310) ~[hibernate-entitymanager-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:273) ~[hibernate-entitymanager-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.ejb.criteria.CriteriaQueryCompiler$3.getResultList(CriteriaQueryCompiler.java:254) ~[hibernate-entitymanager-4.2.1.Final.jar:4.2.1.Final]
    at com.abcd.data.model.filter.JPAQueryBuilder.retrieveResults(JPAQueryBuilder.java:333) ~[classes/:na]
    at com.abcd.data.repo.trend.TrendDao.getTrend(TrendDao.java:33) ~[classes/:na]
    at com.abcd.data.repo.trend.TrendDao$$FastClassByCGLIB$$b47f3dbd.invoke(<generated>) ~[spring-core-3.2.2.RELEASE.jar:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698) ~[spring-aop-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) ~[spring-aop-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155) ~[spring-tx-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) ~[spring-aop-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.cache.interceptor.CacheInterceptor$1.invoke(CacheInterceptor.java:58) ~[spring-context-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:214) ~[spring-context-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:66) ~[spring-context-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) ~[spring-aop-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631) ~[spring-aop-3.2.2.RELEASE.jar:3.2.2.RELEASE]
    at com.abcd.data.repo.trend.TrendDao$$EnhancerByCGLIB$$664630bd.getTrend(<generated>) ~[spring-core-3.2.2.RELEASE.jar:na]
    at com.abcd.data.service.trend.TrendService.getTrend(TrendService.java:56) ~[classes/:na]
    at com.abcd.data.service.trend.TrendService.getTrend(TrendService.java:61) ~[classes/:na]
    at com.abcd.data.service.trend.TrendService$1.call(TrendService.java:80) ~[classes/:na]
    at com.abcd.data.service.trend.TrendService$1.call(TrendService.java:77) ~[classes/:na]
    at java.util.concurrent.FutureTask.run(FutureTask.java:262) ~[na:1.7.0_55]
    ... 3 common frames omitted
Caused by: org.hibernate.exception.GenericJDBCException: could not execute query
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:54) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.loader.Loader.doList(Loader.java:2525) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.loader.Loader.doList(Loader.java:2508) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2338) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.loader.Loader.list(Loader.java:2333) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:490) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:355) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:195) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1269) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:264) ~[hibernate-entitymanager-4.2.1.Final.jar:4.2.1.Final]
    ... 23 common frames omitted
Caused by: java.sql.SQLException: Cannot convert value 'testString' from column 2 to TIMESTAMP.
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1078) ~[mysql-connector-java-5.1.25.jar:na]
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989) ~[mysql-connector-java-5.1.25.jar:na]
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:975) ~[mysql-connector-java-5.1.25.jar:na]
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:920) ~[mysql-connector-java-5.1.25.jar:na]
    at com.mysql.jdbc.ResultSetRow.getTimestampFast(ResultSetRow.java:1404) ~[mysql-connector-java-5.1.25.jar:na]
    at com.mysql.jdbc.ByteArrayRow.getTimestampFast(ByteArrayRow.java:127) ~[mysql-connector-java-5.1.25.jar:na]
    at com.mysql.jdbc.ResultSetImpl.getTimestampInternal(ResultSetImpl.java:6592) ~[mysql-connector-java-5.1.25.jar:na]
    at com.mysql.jdbc.ResultSetImpl.getTimestamp(ResultSetImpl.java:6192) ~[mysql-connector-java-5.1.25.jar:na]
    at com.mysql.jdbc.ResultSetImpl.getTimestamp(ResultSetImpl.java:6230) ~[mysql-connector-java-5.1.25.jar:na]
    at com.mchange.v2.c3p0.impl.NewProxyResultSet.getTimestamp(NewProxyResultSet.java:3394) ~[c3p0-0.9.1.2.jar:0.9.1.2]
    at org.hibernate.type.descriptor.sql.TimestampTypeDescriptor$2.doExtract(TimestampTypeDescriptor.java:67) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:64) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:261) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:257) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:247) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.loader.hql.QueryLoader.getResultRow(QueryLoader.java:443) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.loader.hql.QueryLoader.getResultColumnOrRow(QueryLoader.java:427) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:740) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.loader.Loader.processResultSet(Loader.java:942) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.loader.Loader.doQuery(Loader.java:910) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:341) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    at org.hibernate.loader.Loader.doList(Loader.java:2522) ~[hibernate-core-4.2.1.Final.jar:4.2.1.Final]
    ... 32 common frames omitted
Caused by: java.lang.NumberFormatException: testString
    at com.mysql.jdbc.StringUtils.getInt(StringUtils.java:791) ~[mysql-connector-java-5.1.25.jar:na]
    at com.mysql.jdbc.ResultSetRow.getTimestampFast(ResultSetRow.java:1347) ~[mysql-connector-java-5.1.25.jar:na]
    ... 49 common frames omitted

Я нашел ссылки для явного регистрации преобразователя в persistence.xml. Но я пока не использую persistence.xml и хочу продолжить этот путь. Мои классы моделей с аннотацией @Entity обнаруживаются автоматически. Это код, который я использую для этого:

factory.setPackagesToScan("com.abcd.data.model")

Все мои классы @Entity внутри этого пакета (или любого подпакета) обнаруживаются. Но не по DateConverter аннотируется с помощью @Converter. Что мне здесь не хватает.

Я также попытался добавить в свойства следующие свойства:

Properties properties = new Properties();
properties.put("hibernate.archive.autodetection", "class, hbm");
factory.setJpaProperties(properties);

PS: Пожалуйста, не вдавайтесь в подробности, почему я хочу преобразовать случайную строку в дату. Это всего лишь пример.

Ответ 1

Я еще не знаком с JPA 2.1. Однако, согласно вашему стеклу, вы используете hibernate 4.2.1.

Согласно сообщению SO, указанному ниже, и списку версии спящего режима, поддержка JPA 2.1 добавлена ​​в Hibernate 4.3.0

Вы пробовали обновить версию Hibernate?

SO: Какая версия поддержки hibernate jpa 2.1?

Список версий спящего режима: https://hibernate.atlassian.net/browse/HHH?selectedTab=com.atlassian.jira.jira-projects-plugin%3aversions-panel&subset=-1

Ответ 2

Если вы решили настроить JPA вручную (например, вы хотите настроить несколько источников данных), то LocalContainerEntityManagerFactoryBean должен сканировать пакет, в который помещен конвертер.

@Bean
LocalContainerEntityManagerFactoryBean yourEntityManagerFactory() {
    final LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    factoryBean.setDataSource(arcEditorsDataSource());
    final HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
    jpaVendorAdapter.setDatabase(Database.POSTGRESQL);
    factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
    factoryBean.setPackagesToScan(
            "your.model",
            "your.jpa.config" //where converter is placed
    );
    return factoryBean;
}