java - jUnit、嵌入式 Tomcat 和 Hibernate java.lang.ClassCastException

标签 java hibernate tomcat junit jersey

我有一个非常有趣的问题,在过去的两天里一直让我抓狂。

我正在使用jUnit嵌入式Tomcat来测试一些API端点(Jersey)。 我不太喜欢模拟,我进行此设置是为了在尽可能接近生产的条件下测试 API 响应。

当 API 收到调用时,它应该提供相应的响应(找到、未找到等)。这就是 Hibernate 发挥作用的地方。

当我在 Eclipse 中设置的 Tomcat 上运行此命令时,或者当我在远程服务器上的独立 Tomcat 上部署构建(Maven)时,一切正常,但在测试期间在嵌入式 Tomcat 上调用 API 时我收到此错误:

java.lang.ClassCastException: com.models.listing.Listing cannot be cast to com.models.listing.Listing

是的,它是相同的类名。

为了检索 Listing 对象,我使用 Hibernate 标准通过 ID 获取持久对象

Listing listing = session.get(Listing.class, ID);

这是嵌入式 tomcat 设置的样子:

public void start(String appName) throws Exception {
    File root = getRootFolder();
    System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true");
    Path tempPath = Files.createTempDirectory("tomcat-base-dir");
    
    tomcat = new Tomcat();
    tomcat.setPort(0);
    tomcat.enableNaming();
    tomcat.setSilent(true);
    tomcat.setBaseDir(tempPath.toString());
    
    tomcat.getHost().setDeployOnStartup(true);
    tomcat.getHost().setAutoDeploy(true);
    tomcat.getHost().setAppBase(tempPath.toString());
    
    File webContentFolder = new File(root.getAbsolutePath(), "src/main/webapp/");
    if (!webContentFolder.exists()) {
        webContentFolder = Files.createTempDirectory("default-doc-base").toFile();
    }
    
    StandardContext ctx = (StandardContext) tomcat.addWebapp("/" + appName, webContentFolder.getAbsolutePath());
            
    //Disable TLD scanning by default
    if (System.getProperty(Constants.SKIP_JARS_PROPERTY) == null ) {
        System.out.println("disabling TLD scanning");
        StandardJarScanFilter jarScanFilter = (StandardJarScanFilter) ctx.getJarScanner().getJarScanFilter();
        jarScanFilter.setTldSkip("*");
    }
    
    System.out.println("configuring app with basedir: " + webContentFolder.getAbsolutePath());
    
    // Declare an alternative location for your "WEB-INF/classes" dir
    // Servlet 3.0 annotation will work
    File additionWebInfClassesFolder = new File(root.getAbsolutePath(), "target/classes");
    WebResourceRoot resources = new StandardRoot(ctx);
    
    WebResourceSet resourceSet;
    if (additionWebInfClassesFolder.exists()) {
        resourceSet = new DirResourceSet(resources, "/WEB-INF/classes", additionWebInfClassesFolder.getAbsolutePath(), "/");
        System.out.println("loading WEB-INF resources from as '" + additionWebInfClassesFolder.getAbsolutePath() + "'");
    } else {
        resourceSet = new EmptyResourceSet(resources);
    }
    resources.addPreResources(resourceSet);
    ctx.setResources(resources);
    
    //start tomcat
    tomcat.start();
}

这就是 Hibernate 配置的样子:

private static SessionFactory buildSessionFactory() {
    // setup the session factory
    Configuration configuration = new Configuration();
    
    //add annotated classes
    configuration.addAnnotatedClass(Listing.class);
    
    //connection properties
    configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
    configuration.setProperty("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
    configuration.setProperty("hibernate.connection.url", "jdbc:mysql://<some IP>:3306/<some db name>");
    configuration.setProperty("hibernate.connection.release_mode", "auto");
    configuration.setProperty("hibernate.connection.username", "<some username>");
    configuration.setProperty("hibernate.connection.password", "<some password>");
    configuration.setProperty("hibernate.connection.provider_class", "org.hibernate.connection.C3P0ConnectionProvider");
    
    //sql properties
    configuration.setProperty("show_sql", "true");
    configuration.setProperty("format_sql", "true");
    
    //misc properties
    configuration.setProperty("hibernate.hbm2ddl.auto", "validate");
    configuration.setProperty("hibernate.current_session_context_class", "thread");
    
    //c3p0 properties
    configuration.setProperty("hibernate.c3p0.min_size", "1");
    configuration.setProperty("hibernate.c3p0.max_size", "10");
    configuration.setProperty("hibernate.c3p0.timeout", "100");
    configuration.setProperty("hibernate.c3p0.max_statements", "50");
    configuration.setProperty("hibernate.c3p0.idle_test_period", "1000");
    configuration.setProperty("hibernate.c3p0.validate", "true");
    
    //return null
    return configuration.buildSessionFactory();
}

总而言之,测试在 jUnit 中开始,嵌入式 tomcat 实例启动,并使用 REST 客户端向 API 端点发送请求。端点从 Hibernate 检索资源后做出响应。

依赖版本:

Glassfish Jersey 2.23

jUnit 4.11

Tomcat embedded 8.5.3

Hibernate 5.2.1

我最好的选择是类加载器存在一些问题。 我知道,如果类是用不同的类加载器加载的,那么 JVM 会将类视为不同的类,即使它基本上是来自同一个包的相同类等等,但我似乎找不到一种方法来实现这一点。

也许我的假设完全错误,我在这里遗漏了一些东西,所以如果有人遇到过类似的事情或有一些建议(我已经尝试了数十个“解决方案”)请加入。

提前感谢大家的帮助!

最佳答案

我仍然没有弄清楚其中的奥秘,在使用类加载器等进行了一些其他“反复试验”的想法之后,我就放弃了。

我通过降级到 hibernate 4.3.11 来“修复”它

关于java - jUnit、嵌入式 Tomcat 和 Hibernate java.lang.ClassCastException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38420356/

相关文章:

java - 使用 java 的大型日志文件的“Tail -10”实现

java - ClassNotFoundException - org.aspectj.lang.annotation.Around

java - 如何从 java 调用 Web 服务(由 wsdl 描述)

java - Spring MVC : @ManyToMany DAO?

java - org.springframework.beans.NotReadablePropertyException :

tomcat - 使用 Apache mod_jk.c 进行负载平衡 (3791) : missing uri map for localhost

java - 使用 com.sun.net.httpserver.HttpServer 时不会调用 ServletContextListener

java - 如何在JPA/Hibernate中根据两个外键生成id?

java - 服务器运行java servlet

java - 在服务器启动时在 servlet 中调用 Thread.sleep 时 Tomcat 无法启动