【问题标题】:Cannot configure Hibernate with MySQL and TomEE in Java EE (wrong dialect?)无法在 Java EE 中使用 MySQL 和 TomEE 配置 Hibernate(错误的方言?)
【发布时间】:2020-03-06 14:15:30
【问题描述】:

我正在学习 Java EE,这两天我一直在努力配置 Hibernate,以便在一个简单的 Java EE Web 应用程序中使用 TomEE 服务器上的 MySQL 数据库。

  • Hibernate 版本:5.4.10.Final(核心和实体管理器依赖项)
  • Java EE API:8.0
  • MySQL 版本:8.0.19
  • TomEE 版本:8.0.1(TomEE 嵌入在 tomee-embedded-maven-plugin 中)

我有 2 个简单的实体:Car 和 Seat,具有从 Car 到 Seat 的单向 @OneToMany 关系。 Color 和 EngineType 是普通枚举,而 Specification 是这两个枚举的值对象)。注意:FetchType.EAGER 在@OneToMany 中用于学习目的,我知道这通常不是一个好的解决方案。

当我尝试配置persistence.xml 文件时,alghouth 指定了“与 MySQL 一样”的所有内容,似乎 Hibernate 仍然使用默认的 HSQLDB 语法/方言/引擎,因此,我在架构创建过程中收到错误

[INFO] TomEE embedded started on localhost:8080
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
Hibernate: alter table Seat drop foreign key FKkkm9pdx9e1t9jva76n9tqhhqv
mar 06, 2020 1:48:31 PM org.hibernate.tool.schema.internal.ExceptionHandlerLoggedImpl handleException
WARN: GenerationTarget encountered exception accepting command : Error executing DDL "alter table Seat drop foreign key FKkkm9pdx9e1t9jva76n9tqhhqv" via JDBC Statement
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "alter table Seat drop foreign key FKkkm9pdx9e1t9jva76n9tqhhqv" via JDBC Statement
(...)
Caused by: java.sql.SQLSyntaxErrorException: user lacks privilege or object not found: PUBLIC.SEAT
(...)
Caused by: org.hsqldb.HsqlException: user lacks privilege or object not found: PUBLIC.SEAT     // what is going on here???

对于以后创建 DDL 时的 Hibernate SQL 命令,我也有:

Hibernate: create table Car (identifier bigint not null auto_increment, color varchar(255), engineType varchar(255), primary key (identifier)) engine=InnoDB
mar 06, 2020 1:48:31 PM org.hibernate.tool.schema.internal.ExceptionHandlerLoggedImpl handleException
WARN: GenerationTarget encountered exception accepting command : Error executing DDL "create table Car (identifier bigint not null auto_increment, color varchar(255), engineType varchar(255), primary key (identifier)) engine=InnoDB" via JDBC Statement
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "create table Car (identifier bigint not null auto_increment, color varchar(255), engineType varchar(255), primary key (identifier)) engine=InnoDB" via JDBC Statement
(...)
Caused by: java.sql.SQLSyntaxErrorException: unexpected token: AUTO_INCREMENT
(...)
Caused by: org.hsqldb.HsqlException: unexpected token: AUTO_INCREMENT   // definitely something messed up, this is a correct MySQL token

当我省略所有“MySQL 的东西”(驱动程序和方言)并让 Hibernate 使用默认的 HSQLDB 时,它工作正常...... HSQLDB 的 DDL 已创建良好。

我尝试了许多不同的配置和替换,通过网络和 SO 搜索它,但没有找到任何提示为什么 Hibernate 仍然使用非 MySQL 语法(但我不能 100% 确定这是问题的直接原因)。

我试图在 webapp 下的 resources.xml 文件中指定数据源,但它并没有改变任何东西。 此外,我检查了是否可以从 Maven 构建中排除 HSQLDB,但它附带了 hibernate -core,所以它不容易实现,也许也无济于事。

我之前使用过 Hibernate 和 Spring 和 SpringBoot,但是对于 Java EE,我完全迷失了,这就是我问这个问题的原因。

谁能帮助解决这个问题并就所有这些的正确配置提供一些建议?

resources/META-INF下我的persistence.xml是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.2"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
    <persistence-unit name="my-persistence-unit" transaction-type="JTA">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>java:openejb/Resource/myJtaDatabase</jta-data-source>
        <!-- Entity classes -->
        <class>com.example.javaeecourse.entity.Car</class>
        <class>com.example.javaeecourse.entity.Seat</class>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
            <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/javaeecourse?serverTimezone=UTC" />
            <property name="hibernate.connection.username" value="root" />
            <property name="hibernate.connection.password" value="admin" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.hbm2ddl.auto" value="create"/>
            <property name="tomee.jpa.factory.lazy" value="true" />
            <!--<property name="tomee.jpa.cdi=false" value="false" />-->
        </properties>
    </persistence-unit>
</persistence>

汽车实体:

package com.example.javaeecourse.entity;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

import static com.example.javaeecourse.entity.Car.FIND_ALL;

@Getter
@Setter
@Entity
@Table(name = "cars")
@NamedQuery(name = FIND_ALL, query = "SELECT car FROM Car car")
@NoArgsConstructor
public class Car {

    public static final String FIND_ALL = "Car.findAll";

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long identifier;

    @Enumerated(EnumType.STRING)
    private Color color;
    @Enumerated(EnumType.STRING)
    private EngineType engineType;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "car", nullable = false)
    private Set<Seat> seats = new HashSet<>();
}

座位实体:

package com.example.javaeecourse.entity;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;

@Entity
@Table(name = "seats")
@NoArgsConstructor
@Getter
@Setter
public class Seat {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String seatMaterial;

    public Seat(String seatMaterial) {
        this.seatMaterial = seatMaterial;
    }
}

CarManufacturer 类(@Stateless EJB,其中调用了 EntityManager):

package com.example.javaeecourse.boundary;

import com.example.javaeecourse.control.CarFactory;
import com.example.javaeecourse.entity.Car;
import com.example.javaeecourse.entity.CarCreatedEvent;
import com.example.javaeecourse.entity.Specification;

import javax.ejb.Stateless;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;

@Stateless
public class CarManufacturer {

    @Inject
    CarFactory carFactory;

    @Inject
    Event<CarCreatedEvent> carCreatedEvent;

    @PersistenceContext
    EntityManager entityManager;

    public Car manufactureCar(Specification specification) {
        Car car = carFactory.createCar(specification);
        entityManager.persist(car);
        carCreatedEvent.fire(new CarCreatedEvent(car.getIdentifier()));
        return car;
    }

    public List<Car> retrieveCars() {
        return entityManager.createNamedQuery(Car.FIND_ALL, Car.class).getResultList();
    }
}

CarFactory 类,实例化实体:

package com.example.javaeecourse.control;

import com.example.javaeecourse.entity.*;

import javax.inject.Inject;

public class CarFactory {

    @Inject
    @DefaultCarColor
    Color randomCarColor;

    @Inject
    @DefaultCarEngineType
    EngineType randomCarEngineType;

    public Car createCar(Specification specification) {
        Car car = new Car();
        car.setColor(specification.getColor() == null ? randomCarColor : specification.getColor());
        car.setEngineType(specification.getEngineType() == null ? randomCarEngineType : specification.getEngineType());
        Seat seat = new Seat("Leather");
        car.getSeats().add(seat);
        return car;
    }
}

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>

<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>com.example</groupId>
  <artifactId>javaeecourse</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>Java EE Course App</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <failOnMissingWebXml>false</failOnMissingWebXml>
  </properties>

  <dependencies>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
      <version>8.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
      <version>8.0.19</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>5.4.10.Final</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>5.4.10.Final</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.12</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.jaxrs</groupId>
      <artifactId>jackson-jaxrs-json-provider</artifactId>
      <version>2.9.0</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>javaeecourse</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <groupId>org.apache.tomee.maven</groupId>
          <artifactId>tomee-embedded-maven-plugin</artifactId>
          <version>8.0.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

如果有任何帮助,我将不胜感激。

【问题讨论】:

  • 当您为数据源提供jta-datasource 元素时,您不必提供任何hibernate.connection 属性。检查数据源在 TomeEE 上的配置是否正确,并且 jdbc 驱动程序在那里。
  • @areus 非常感谢您的提示!我能够解决此案(为该问题的未来读者发布了答案)。

标签: mysql hibernate jpa apache-tomee java-ee-8


【解决方案1】:

感谢@areus 在问题评论中提供的提示,我得以解决此案。如果将来有人遇到类似问题,我会在此处发布答案。

有 1 个主要问题和 1 个“不良做法”问题。

主要问题是我使用的是 JTA 事务类型并通过 JNDI 名称引用了 jta-data-source,但 resources.xml 位于 webapp 下而不是 resources/META-INF。 将 resource.xml 移至 META-INF 后,可以正确创建数据库并持久化实体。

另外,在我的配置中,我使用了一个已弃用的 MySQL 8 JDBC 驱动程序。根据this 文档,从 MySQL 8.0 开始,驱动程序现在是 com.mysql.cj.jdbc.Driver,而不是 com.mysql.jdbc.Driver

我还删除了不必要的属性,所以现在配置很清晰,一切正常。

资源/META-INF/persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.2"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
    <persistence-unit name="my-persistence-unit" transaction-type="JTA">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>jdbc/javaeecourse</jta-data-source>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
            <property name="tomee.jpa.factory.lazy" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

资源/META-INF/resources.xml

<?xml version="1.0" encoding="UTF-8"?>
<tomee>
<Resource id="jdbc/javaeecourse" type="javax.sql.DataSource">
    JdbcDriver = com.mysql.cj.jdbc.Driver
    JdbcUrl = jdbc:mysql://localhost:3306/javaeecourse?serverTimezone=UTC
    UserName = root
    Password = admin
    jtaManaged = true
</Resource>
</tomee>

这里还有一个great article,关于 JTA/RESOURCE_LOCAL 事务类型以及 jta-data-source 和 non-jta-data-source 配置,帮助我了解了细节。

【讨论】:

    猜你喜欢
    • 2015-09-19
    • 1970-01-01
    • 2018-01-24
    • 2016-04-29
    • 2017-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-01
    相关资源
    最近更新 更多