2016-02-15 2 views
2

J'ai récemment mis à niveau une application Spring Boot basée sur Hibernate 4 vers Hibernate 5. Depuis, j'observe un problème de chargement de classe. Évidemment, les classes hibernate et ma classe de domaine sont chargées par deux chargeurs de classe différents. Cela ne se produit que si je lance l'application avec Spring DevTools et Hibernate 5. Les combinaisons DevTools/Hibernate 4, mvn spring-boot: run/Hibernate 5 fonctionnent.Erreur de chargement de classe avec Spring Boot et Hibernate 5

Le problème peut être reproduit avec l'application simple de démarrage au printemps suivant (l'ensemble du projet Eclipse est disponible here)

@Entity 
public class Employee implements Serializable { 
    private static final long serialVersionUID = 1L; 

    private Long  id; 
    private String firstName; 
    private String lastName; 

    public Employee() { 
    } 

    public Employee(String firstName, String lastName) { 
     this.firstName = firstName; 
     this.lastName = lastName; 
    } 

    @Id @GeneratedValue 
    public Long getId() { 
     return id; 
    } 

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

    public String getFirstName() { 
     return firstName; 
    } 

    public void setFirstName(String firstName) { 
     this.firstName = firstName; 
    } 

    public String getLastName() { 
     return lastName; 
    } 

    public void setLastName(String lastName) { 
     this.lastName = lastName; 
    } 

    public String toString() { 
     return id + ": " + lastName + ", " + firstName; 
    } 
} 

public class AppConfig { 

    @Autowired 
    private DataSource dataSource; 

    @SuppressWarnings("serial") 
    @Bean 
    public LocalSessionFactoryBean sessionFactory() { 

     LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); 
     sessionFactory.setDataSource(dataSource); 
     sessionFactory.setPackagesToScan("problem.domain"); 
     sessionFactory.setHibernateProperties(new Properties() { 
      { 
       setProperty("hibernate.dialect", "org.hibernate.dialect.DerbyTenSevenDialect"); 
       setProperty("hibernate.hbm2ddl.auto", "create-drop"); 
       setProperty("hibernate.current_session_context_class", "thread"); 
      } 
     }); 

     return sessionFactory; 
    } 
} 

@Component 
public class DatabaseInitializer implements ApplicationRunner { 

    @Autowired 
    private SessionFactory sessionFactory; 

    @Override 
    public void run(ApplicationArguments args) throws Exception { 

     Session session = sessionFactory.getCurrentSession(); 
     Transaction tx = session.beginTransaction(); 
     Employee empl = new Employee("John", "Doe"); 
     session.persist(empl); 
     tx.commit(); 
    } 
} 

@SpringBootApplication 
public class SpringBootMain { 

    public static void main(String[] args) throws Exception { 
     SpringApplication.run(SpringBootMain.class, args); 
    } 
} 

pom.xml: 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 

    <groupId>swt6.spring</groupId> 
    <artifactId>hibernate5-problem</artifactId> 
    <packaging>jar</packaging> 
    <version>1.0.0-SNAPSHOT</version> 

    <parent> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-parent</artifactId> 
     <version>1.3.2.RELEASE</version> 
     <relativePath /> <!-- lookup parent from repository --> 
    </parent> 

    <properties> 
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
     <java.version>1.8</java.version> 
     <hibernate.version>5.1.0.Final</hibernate.version> 
     <derby.version>10.12.1.1</derby.version> 
    </properties> 

    <dependencies> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-data-jpa</artifactId> 
     </dependency> 

     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-devtools</artifactId> 
     </dependency> 

     <dependency> 
      <groupId>org.apache.derby</groupId> 
      <artifactId>derby</artifactId> 
     </dependency> 

    </dependencies> 

    <build> 
     <plugins> 
      <plugin> 
       <groupId>org.springframework.boot</groupId> 
       <artifactId>spring-boot-maven-plugin</artifactId> 
      </plugin> 
     </plugins> 
    </build> 

</project> 

L'exécution de ce programme avec des résultats Spring DevTools dans l'erreur suivante:

2016-02-15 18:30:48.315 INFO 13828 --- [ restartedMain] o.h.t.schema.internal.SchemaCreatorImpl : HHH000476: Executing import script 'org.hiber[email protected]55ad1b60' 
2016-02-15 18:30:48.509 INFO 13828 --- [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer  : LiveReload server is running on port 35729 
2016-02-15 18:30:48.536 INFO 13828 --- [ restartedMain] o.s.j.e.a.AnnotationMBeanExporter  : Registering beans for JMX exposure on startup 
2016-02-15 18:30:48.582 ERROR 13828 --- [ restartedMain] o.h.p.access.spi.GetterMethodImpl  : HHH000122: IllegalArgumentException in class: problem.domain.Employee, getter method of property: id 
2016-02-15 18:30:48.583 ERROR 13828 --- [ restartedMain] o.s.boot.SpringApplication    : Application startup failed 

java.lang.IllegalStateException: Failed to execute ApplicationRunner 
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:800) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE] 
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:787) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE] 
    at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:777) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE] 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE] 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1191) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE] 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1180) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE] 
    at problem.main.SpringBootMain.main(SpringBootMain.java:10) [classes/:na] 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_66] 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_66] 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_66] 
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_66] 
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-1.3.2.RELEASE.jar:1.3.2.RELEASE] 
Caused by: org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of problem.domain.Employee.id 
    at org.hibernate.property.access.spi.GetterMethodImpl.get(GetterMethodImpl.java:64) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final] 
    at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:223) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final] 
    at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:4633) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final] 
    at org.hibernate.persister.entity.AbstractEntityPersister.isTransient(AbstractEntityPersister.java:4344) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final] 
    at org.hibernate.engine.internal.ForeignKeys.isTransient(ForeignKeys.java:226) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final] 
    at org.hibernate.event.internal.AbstractSaveEventListener.getEntityState(AbstractSaveEventListener.java:499) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final] 
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:99) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final] 
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final] 
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:778) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final] 
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:751) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final] 
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:756) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final] 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_66] 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_66] 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_66] 
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_66] 
    at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:338) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final] 
    at com.sun.proxy.$Proxy56.persist(Unknown Source) ~[na:na] 
    at problem.main.DatabaseInitializer.run(DatabaseInitializer.java:24) ~[classes/:na] 
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:797) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE] 
    ... 11 common frames omitted 
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_66] 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_66] 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_66] 
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_66] 
    at org.hibernate.property.access.spi.GetterMethodImpl.get(GetterMethodImpl.java:41) ~[hibernate-core-5.1.0.Final.jar:5.1.0.Final] 
    ... 29 common frames omitted 

2016-02-15 18:30:48.585 INFO 13828 --- [ restartedMain] .b.l.ClasspathLoggingApplicationListener : Application failed to start with classpath: [file:/D:/P20058/Documents/FH/Lehre/SWT6U/Uebungen/SpringWeb/hibernate5-problem/target/classes/] 
2016-02-15 18:30:48.585 INFO 13828 --- [ restartedMain] utoConfigurationReportLoggingInitializer : 

Error starting ApplicationContext. To display the auto-configuration report enable debug logging (start with --debug) 


2016-02-15 18:30:48.585 INFO 13828 --- [ restartedMain] s.c.a.AnnotationConfigApplicationContext : Closing org.spring[email protected]63e38bca: startup date [Mon Feb 15 18:30:46 CET 2016]; root of context hierarchy 
2016-02-15 18:30:48.587 INFO 13828 --- [ restartedMain] o.s.j.e.a.AnnotationMBeanExporter  : Unregistering JMX-exposed beans on shutdown 
2016-02-15 18:30:48.587 INFO 13828 --- [ restartedMain] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed drop of schema as part of SessionFactory shut-down' 
2016-02-15 18:30:48.604 INFO 13828 --- [ restartedMain] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 
2016-02-15 18:30:48.604 INFO 13828 --- [ restartedMain] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed drop of schema as part of SessionFactory shut-down' 

Répondre

2

Il existe un problème de prise en charge d'Hibernate 5 avec Spring Boot here.

Dans votre exemple, il y a deux classloaders:

  1. Le système classloader
  2. Spring Boot DevTools classloader, supportant la fonction de redémarrage

La valeur par défaut de mettre en œuvre des classes de résoudre Hibernate ClassLoaderService d'abord regardant dans son propre classloader, puis le classloader de printemps. Votre classe est chargée par spring (avec le chargeur de classe de redémarrage), donnée à hiberner via l'unité persistante, mais hibernate recharge cette classe avec son ClassLoaderService, et la trouve dans son propre classloader (le système cl). Deux classes sont chargées, et la conséquence est l'erreur que vous avez vue. Spring peut être configuré pour charger hibernate dans le chargeur de classe de redémarrage, mais je n'ai pas réussi à isoler un ensemble de bibliothèques: en ajoutant seulement hibernate- * fail avec des erreurs de spring-orm ou un EntityManager non visible du proxybuilder.

Une solution de travail (mais vraiment laid!): Ajouter à META-INF/spring-devtools.properties

restart.include.all=.* 

Je suppose qu'il ya une meilleure solution que celle-ci