(1) 学习 JPA 开山篇 (包含未解的错误?)

本文详细介绍了JPA(Java Persistence API)作为Java持久化规范的背景、核心思想以及在实际开发中的应用案例。重点阐述了JPA的技术特点、与现有ORM框架的整合、配置文件设置及实体类映射过程,并通过测试类展示了如何利用JPA创建表。此外,还分析了一个遇到的问题及其解决方案,揭示了JPA在开发中的优势与挑战。

一:JPA简介


          JPA(Java Persistence API)是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关系映射工具来管理Java应用中的关系数据。他的出现主要是为了简化现有的持久化开发工作和整合ORM技术,结束现在Hibernate、TopLink等ORM框架各自为营的局面。值得注意的是,JPA是在充分吸收了现有Hibernate、TopLink等ORM框架的基础上发展而来的,具有易于使用、伸缩性强等优点。从目前的开发社区的反应上看,JPA受到了极大的支持和赞扬,其中就包括了Spring与EJB3.0的开发团队。着眼未来几年的技术走向,JPA作为ORM领域标准化整合者的目标应该不难实现。


二:JPA技术的思想和提示:

       1. JPA技术:
        JPA的总体思想和现有Hibernate、TopLink等ORM框架大体一致。总的来说,JPA包括以下3方面的技术:
        <1> ORM映射元数据
JPA支持XML和JDK 5.0注释(也可译作注解)两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中。
         <2> Java持久化API
用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者可以从繁琐的JDBC和SQL代码中解脱出来。
        <3> 查询语言
这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。
       提示: JPA不是一种新的ORM框架,他的出现只是用于规范现有的ORM技术,他不能取代现有的Hibernate、TopLink等ORM框架。相反,在采用JPA开发时,我们仍将使用到这些ORM框架,只是此时开发出来的应用不再依赖于某个持久化提供商。应用可以在不修改代码的情况下在任何JPA环境下运行,真正做到低耦合,可扩展的程序设计。


三:hibernate下的JPA需要的jar包


四:hibernate下的jpa的配置文件;


<persistence xmlns="http://java.sun.com/xml/ns/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_1_0.xsd"  
       version="1.0">  
       
       <persistence-unit name="itm" transaction-type="RESOURCE_LOCAL">  
	          <properties>  
		             <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>  
		             <property name="hibernate.hbm2ddl.auto" value="update"/>  
		             <property name="hibernate.connection.driver_class" value="org.gjt.mm.mysql.Driver"/>  
		             <property name="hibernate.connection.username" value="root"/>  
		             <property name="hibernate.connection.password" value="root"/>  
		             <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/itm?useUnicode=true&characterEncoding=UTF-8"/>  
	          </properties>  
      </persistence-unit>  
</persistence> 

itm:为数据库名。

实体类:

package cn.itm.bean;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Person {

	private Integer id;
	
	private String mingzi;
	
	public Person(){}

	public Person(String mingzi) {
		this.mingzi = mingzi;
	}

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

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

	public String getMingzi() {
		return mingzi;
	}

	public void setMingzi(String mingzi) {
		this.mingzi = mingzi;
	}

	
}

测试类:以及如何映射实体bean(采用第二种)来创建表。

两种建模的方式:

package junit.test;


import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.junit.BeforeClass;
import org.junit.Test;

import cn.itm.bean.Person;

public class PersonTest {

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}
	
	@Test public void save(){
		// 类似  sessionFactory。---->session---->begin事物.
		EntityManagerFactory factory = Persistence.createEntityManagerFactory("itm");
		
		EntityManager em = factory.createEntityManager();
		em.getTransaction().begin(); // 开启事务。
		em.persist(new Person("大记号"));
		em.getTransaction().commit();
		em.close();
		
		factory.close();
	}

}

结果:可以创建一个表,却无法执行 增删改查操作:


郁闷-------------------》

javax.persistence.PersistenceException: org.hibernate.exception.JDBCConnectionException: Cannot open connection
    at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:614)
    at org.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:41)
    at junit.test.PersonTest.save(PersonTest.java:24)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:73)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:46)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
    at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.hibernate.exception.JDBCConnectionException: Cannot open connection
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:97)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:52)
    at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:449)
    at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:167)
    at org.hibernate.jdbc.JDBCContext.connection(JDBCContext.java:142)
    at org.hibernate.transaction.JDBCTransaction.begin(JDBCTransaction.java:85)
    at org.hibernate.impl.SessionImpl.beginTransaction(SessionImpl.java:1353)
    at org.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:38)
    ... 25 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

Last packet sent to the server was 0 ms ago.
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
    at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1074)
    at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2103)
    at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:718)
    at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:46)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
    at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:302)
    at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:282)
    at java.sql.DriverManager.getConnection(DriverManager.java:582)
    at java.sql.DriverManager.getConnection(DriverManager.java:154)
    at org.hibernate.connection.DriverManagerConnectionProvider.getConnection(DriverManagerConnectionProvider.java:133)
    at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:446)
    ... 30 more
Caused by: java.net.ConnectException: Connection refused: connect
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
    at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
    at java.net.Socket.connect(Socket.java:519)
    at java.net.Socket.connect(Socket.java:469)
    at java.net.Socket.<init>(Socket.java:366)
    at java.net.Socket.<init>(Socket.java:209)
    at com.mysql.jdbc.StandardSocketFactory.connect(StandardSocketFactory.java:253)
    at com.mysql.jdbc.MysqlIO.<init>(MysqlIO.java:280)
    at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2026)
    ... 43 more




<think>好的,我现在需要解决用户提出的JPA中MissingIdentifierException错误的问题,也就是TableIdNotFound错误。首先,我需要回顾之前的对话,确保我理解用户的问题背景。用户之前询问过TableIdNotFound错误的原因和解决方案,现在他们想解决JPA中的MissingIdentifierException,这实际上是同一类问题,但具体在JPA框架下。 首先,我应该确认JPA中导致MissingIdentifierException的常见原因。根据之前的回答,可能的原因包括实体类缺少主键注解、数据库表与实体映射不一致、主键生成策略配置错误等。结合用户提供的站内引用,特别是引用[1]、[2]、[4]和[5],我需要将这些信息整合到解决方案中。 用户提供的引用[1]提到实体类字段命名问题,需要符合驼峰规则,否则JPA无法找到属性。这可能与主键字段的命名有关,如果主键字段的命名不符合规范,也可能导致JPA无法识别主键,从而抛出MissingIdentifierException。引用[4]提到创建Repository时的bean创建错误,可能涉及到实体类配置问题,例如缺少必要注解。这些信息都需要考虑进去。 接下来,我需要组织解决方案的结构,确保逻辑清晰。可能的步骤包括检查主键注解是否正确应用、验证数据库表结构、核对主键生成策略、检查实体类字段命名是否符合规范、查看日志和配置等。此外,用户之前的问题中提到了MyBatis-Plus的解决方案,现在需要转换为JPA的对应方法,例如使用@Id和@GeneratedValue注解。 需要注意用户提到的引用[3]关于JPA保存null的问题,虽然与当前问题不直接相关,但可能间接影响实体类的配置,比如是否需要特殊处理某些字段。引用[5]涉及分页查询的错误处理,可能不直接相关,但可以忽略。 然后,我需要确保回答符合用户的要求,即结构清晰,分步骤说明,并引用提供的站内资料。例如,在检查主键注解时,可以引用引用[1]提到的字段命名问题,指出主键字段的命名必须符合规范,并且正确使用@Id注解。在数据库表结构验证部分,可以结合引用[2]中的自动更新问题,说明数据库和实体类映射的重要性。 此外,需要生成相关问题,这些问题应围绕JPA主键配置、常见错误处理等,例如询问JPA中主键生成策略的类型,如何解决实体类与表映射不一致的问题,或者如何处理JPA的自动更新行为。需要确保问题与当前主题相关,并且参考之前的对话和引用内容。 最后,检查回答是否符合格式要求:使用中文,正确使用LaTeX格式,引用标记正确,代码块格式正确,以及相关问题部分的结构正确。确保没有使用任何Markdown格式,除了用户指定的代码块和引用标识。</think>#### 一、JPA中`MissingIdentifierException`错误的核心原因 该错误表明JPA无法识别实体类的主键定义,本质是**主键映射缺失或配置错误**。以下是具体原因分类: 1. **主键注解缺失** JPA要求实体类必须使用`@Id`标注主键字段。若未标注,会直接触发此错误。例如: ```java // 错误示例:缺少@Id注解 @Entity public class User { private Long userId; // 未标注主键 private String name; } ``` 2. **字段命名不符合JPA规范** 根据引用[1],JPA通过属性名解析数据库字段时,要求**严格遵循驼峰命名规则**。例如: - 若数据库字段为`user_id`,实体类属性应命名为`userId`(而非`userid`或`UserID`) - 错误命名会导致JPA无法映射主键字段[^1] 3. **主键生成策略冲突** 使用`@GeneratedValue`时,若配置策略与数据库不兼容(如MySQL使用`SEQUENCE`),会引发映射失败: ```java @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) // MySQL不支持序列生成 private Long id; ``` 4. **实体类与数据库表结构不匹配** 如引用[2]所述,JPA会自动同步实体状态到数据库。若表结构缺少主键列或命名不一致,会导致元数据读取失败[^2] --- #### 二、系统化解决方案 ##### 步骤1:检查主键注解配置 ```java @Entity @Table(name = "t_user") public class User { @Id // 必须添加@Id注解 @GeneratedValue(strategy = GenerationType.IDENTITY) // 明确主键生成策略 @Column(name = "user_id") // 显式指定数据库列名 private Long userId; private String userName; } ``` ##### 步骤2:验证字段命名规范 | 场景 | 正确示例 | 错误示例 | |---------------------|-------------------|------------------| | 数据库字段名 | `user_name` | `UserName` | | 实体类属性名 | `userName` | `username` | | 查询方法命名 | `findByUserName` | `findByusername` | > **注意**:若数据库字段使用下划线命名,必须通过`@Column(name="...")`显式映射(引用[1]) ##### 步骤3:核对主键生成策略 根据数据库类型选择策略: ```java // MySQL适用自增策略 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // Oracle适用序列生成 @Id @SequenceGenerator(name="seq", sequenceName="user_seq") @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq") private Long id; ``` ##### 步骤4:检查数据库表结构 执行SQL验证主键存在性: ```sql SHOW CREATE TABLE t_user; -- 确认主键列`user_id`存在 ALTER TABLE t_user ADD PRIMARY KEY (user_id); -- 缺失时添加主键 ``` ##### 步骤5:检查JPA配置 在`application.properties`中启用调试日志: ```properties spring.jpa.show-sql=true logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE ``` --- #### 三、典型错误场景修复 **场景1:复合主键未正确定义** ```java // 错误:未使用@IdClass或@EmbeddedId @Entity public class OrderItem { @Id private Long orderId; @Id // 复合主键需要特殊处理 private Long productId; } ``` **修复方案:** ```java @IdClass(OrderItemPK.class) // 定义主键类 @Entity public class OrderItem { @Id private Long orderId; @Id private Long productId; } // 主键类必须实现Serializable public class OrderItemPK implements Serializable { private Long orderId; private Long productId; } ``` **场景2:Lombok注解冲突** 若使用Lombok的`@Data`但未正确生成getter/setter,可能导致JPA无法识别字段: ```java // 错误示例:Lombok生成错误的方法名 @Data @Entity public class Product { @Id private Long prodId; // Lombok生成getProdId(),但JPA可能期望getProdID() } ``` **解决方案:** 使用显式`@Getter/@Setter`或手动定义访问方法 --- #### 四、进阶排查技巧 1. **检查JPA元数据** 通过`EntityManagerFactory`获取元数据: ```java Metamodel metamodel = entityManager.getMetamodel(); EntityType<User> userEntity = metamodel.entity(User.class); SingularAttribute<? super User, ?> idAttr = userEntity.getId(Long.class); // 验证主键是否被识别 ``` 2. **版本兼容性检查** 如引用[4]所述,Spring Data JPA版本与Hibernate的兼容性问题可能导致元数据解析错误[^4] ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值