Java SE环境中的JPA:在persistence.xml中加载没有定义的实体类

时间:2014-03-27 11:54:33

标签: java hibernate jpa

我正在准备Oracle JPAD认证,所以我在Java SE环境中与实体玩游戏(至少,这就是我的想法......)。我注意到了某些事情,我希望有人能够对此有所了解。

Pro JPA2这本书正是我所读的。在第2章末尾的某处,有一个示例persistence.xml文件,其中定义了一个类。好的,这是Java SE环境的最佳选择,因为你并没有真正拥有容器中的整个shebang。但是这里有一个奇怪的事情:我有两个用@Entity注释的Entity类,并且没有将任何这些类添加到persistence.xml中。但仍然正在加载!我很惊讶。我现在唯一能想到的是,我不是在SE环境中,或者它是一个新的休眠功能......

的persistence.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
  version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
  <persistence-unit name="employees" transaction-type="RESOURCE_LOCAL">

    <properties>
      <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver" />
      <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:mem:." />
      <property name="javax.persistence.jdbc.user" value="SA" />
      <property name="javax.persistence.jdbc.password" value="" />

      <property name="hibernate.show_sql" value="false" />
      <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
      <property name="hibernate.connection.shutdown" value="true" />
      <property name="hibernate.hbm2ddl.auto" value="create-drop" />
    </properties>

  </persistence-unit>
</persistence> 

实体:

@Entity
   @Access(AccessType.FIELD)
   public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private Date startDate;
    private Long salary;

    public Employee() {
    }

    public Employee(String name) {
        this.name = name;
    }

    public Employee(String name, Long salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getStartDate() {
        return startDate;
    }

    public void setStartDate(Date startDate) {
        this.startDate = startDate;
    }

    public Long getSalary() {
        return salary;
    }

    public void setSalary(Long salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee [name=" + name + ", salary=" + salary + ", startDate=" + startDate + "]";
    }
}

测试:

public class TestConnection {

    private static EntityManagerFactory entityManagerFactory;
    private static EntityManager entityManager;
    private static final String PERSISTENCE_UNIT_NAME = "employees";

    @BeforeClass
    public static void initEntityManager() throws Exception {
        entityManagerFactory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
        entityManager = entityManagerFactory.createEntityManager();
        TableStructurePrinter.outputTableStructure(entityManager);
    }

    @AfterClass
    public static void closeEntityManager() {
        entityManager.close();
        entityManagerFactory.close();
    }

    @BeforeMethod
    public void setupDB() throws Exception {
        entityManager.getTransaction().begin();
        LoadEmployees.load(entityManager, 4);
    }

    @AfterMethod
    public void cleanDB() throws Exception {
        entityManager.getTransaction().rollback();
    }

    @Test
    public void testCountEmployees() {
        Query q = entityManager.createQuery("select e from Employee e");
        assertEquals(q.getResultList().size(), 4);
    }

}

我的TableStructurePrinter的输出(这是来自INFORMATION_SCHEMA.SYSTEM_TABLES和INFORMATION_SCHEMA.SYSTEM_COLUMNS的简单select语句):

T.TABLE_TYPE         T.TABLE_NAME           C.ORDINAL_POSITION C.COLUMN_NAME                 C.TYPE_NAME        C.COLUMN_SIZE
===================  ===================   =================== ===================   ===================  ===================
TABLE                EMPLOYEE                                1 ID                                 BIGINT                   64
TABLE                EMPLOYEE                                2 NAME                              VARCHAR                  255
TABLE                EMPLOYEE                                3 SALARY                             BIGINT                   64
TABLE                EMPLOYEE                                4 STARTDATE                       TIMESTAMP                   26

2 个答案:

答案 0 :(得分:2)

我注意到你玩Java SE。基本上在Java SE环境中,您必须指定托管类,并且任何其他行为都不可移植(请参阅下面的 8.2.1.6.4托管类列表)。作为参数,我将添加JPA 2.0规范的摘录,它实际上解释了Hibernate(或者我测试它的EclipseLink)是如何工作的(尽管不是可移植的)以及你应该如何:

由持久性单元管理的托管持久性类集是通过使用以下一个或多个来定义的:

8.2.1.6 mapping-file,jar-file,class,exclude-unlisted-classes

  
      
  • 包含在持久性单元的根中的带注释的托管持久性类(除非exclude-unlisted-classes元素是   指定)
  •   
  • 一个或多个对象/关系映射XML文件
  •   
  • 将搜索类的一个或多个jar文件
  •   
  • 明确的类列表由持久性单元管理的实体集是这些源的并集,映射元 -   任何给定类的数据注释(或注释默认值)   被XML映射覆盖
  •   
     

由持久性单元管理的实体集是联合体   这些来源[...]

8.2.1.6.1持久性单位根目录中的带注释的类

  

搜索持久性单元根目录中包含的所有类   对于带注释的托管持久化类 - 具有实体的类,   Embeddable或MappedSuperclass注释 - 以及任何映射元数据   在这些类上找到的注释将被处理,或者它们将被处理   使用映射注释默认映射。如果不是这样的话   注释的持久化类包含在根目录中   持久性单元包含在持久性单元中   exclude-unlisted-classes元素必须指定为true。    exclude-unlisted-classes元素不适用于Java SE环境。

8.2.1.6.4托管类列表

  

[...]

     

必须在中指定所有命名的托管持久性类的列表   Java SE环境以确保可移植性。便携式Java SE   应用程序不应该依赖于此处描述的其他机制   指定持久性单元的托管持久性类。   持久性提供程序可能要求实体类和   必须在每个中完全枚举要管理的类   Java SE环境中的persistence.xml文件。

答案 1 :(得分:1)

Hibernate也可以自动检测实体,至少在EE环境中,但我会说SE应用程序也是如此。 AFAIK它将扫描包含persistence.xml文件的jar以及那里列出的任何jar或类。

来自http://docs.oracle.com/cd/E16439_01/doc.1013/e13981/cfgdepds005.htm的引用:

  

此持久性单元包含哪些持久性托管类?     
您可以使用以下一项或多项指定与持久性单元关联的持久性托管类:

     
      
  • &LT;映射-文件&gt; element:指定一个或多个对象关系映射XML文件(orm.xml文件)。

  •   
  • &LT; JAR-文件&gt; element:指定将搜索类的一个或多个JAR文件。

  •   
  • &LT;类&GT; element:指定明确的类列表。

  •   
  • 持久化单元的根目录中包含注释的托管持久性类。

         

    持久性单元的根是JAR文件或目录,其META-INF目录包含persistence.xml文件。要排除托管持久性类,请添加&lt; exclude-unlisted-classes&gt;元素到持久性单元。

  •   

最后一部分是与您的案例相关的内容,即托管类(实体)包含在包含persistence.xml的同一个jar中,因此会自动添加。