1、简介 {#1简介}
Spring 为程序员简化 Java 应用程序中的数据库交互提供了一个最强大的框架,那就是 Spring JPA(Java Persistence API)。它为 JPA 提供了一个稳定的抽象。
然而,尽管使用方便,开发人员还是经常会遇到一些错误,而这些错误的排查和解决都非常具有迷惑性。其中一个常见问题就是 "Unable to Locate Attribute with the Given Name" 错误。
本文将带你了解 Spring JPA 出现 "Unable to Locate Attribute with the Given Name" 异常的原因以及解决办法。
2、案例 {#2案例}
我们生产了一个可穿戴的小工具。经过最近的一项调查,我们的营销团队发现,在我们的平台上按传感器类型、价格和受欢迎程度对产品进行分类,可以突出最受欢迎的产品,从而帮助客户做出更好的购买决策。
3、添加 Maven 依赖 {#3添加-maven-依赖}
我们使用 H2 内存数据库在项目中创建一个可穿戴设备表,并将样本数据填充到该表中,以便在接下来的测试中使用。
首先,添加以下 Maven 依赖:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.224</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.7.11</version>
</dependency>
4、应用配置 {#4应用配置}
在 src/main/resources
文件夹中,创建包含以下配置内容的 application-h2.properties
:
# H2 配置
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.hbm2ddl.auto=create-drop
# Spring Datasource URL
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
在 src/main/resources
文件夹中创建名为 testdata.sql
的 SQL 文件,其内容如下,用于在 H2 数据库中创建 wearables
表,其中包含一些预定义条目:
CREATE TABLE IF NOT EXISTS wearables (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
price DECIMAL(10, 2),
sensor_type VARCHAR(255),
popularity_index INT
);
DELETE FROM wearables;
INSERT INTO wearables (id, name, price, sensor_type, popularity_index)
VALUES (1, 'SensaWatch', '500.00', 'Accelerometer', 5);
INSERT INTO wearables (id, name, price, sensor_type, popularity_index)
VALUES (2, 'SensaBelt', '300.00', 'Heart Rate', 3);
INSERT INTO wearables (id, name, price, sensor_type, popularity_index)
VALUES (3, 'SensaTag', '120.00', 'Proximity', 2);
INSERT INTO wearables (id, name, price, sensor_type, popularity_index)
VALUES (4, 'SensaShirt', '150.00', 'Human Activity Recognition', 2);
5、Model 定义 {#5model-定义}
定义 WearableEntity
实体:
@Entity
public class WearableEntity {
@Id @GeneratedValue
private Long Id;
@Column(name = "name")
private String Name;
@Column(name = "price")
private BigDecimal Price;
// 例如,"Heart Rate Monitor(心率监测)"、"Neuro Feedback(神经反馈)" 等。
@Column(name = "sensor_type")
private String SensorType;
@Column(name = "popularity_index")
private Integer PopularityIndex;
}
注意,实体中的字段名称是大写字母开头。
6、定义实体过滤查询 {#6定义实体过滤查询}
实体定义好后,在数据库中添加一个查询,根据过滤条件检索 WearableEntity
。
public interface WearableRepository extends JpaRepository<WearableEntity, Long> {
List<WearableEntity> findAllByOrderByPriceAscSensorTypeAscPopularityIndexDesc();
}
解释如下:
findAllBy
:该方法检索所有属于WearableEntity
或WearableEntity
类型的记录。OrderByPriceAsc
:按价格(price
)升序对结果排序。SensorTypeAsc
:按价格排序后,再按传感器类型(sensorType
)升序排序。PopularityIndexDesc
:最后,按照popularityIndex
从高到低对结果排序(因为受欢迎程度越高越好)
7、通过集成测试来测试 Repository {#7通过集成测试来测试-repository}
现在,在项目中引入集成测试来测试 WearableRepository
的行为:
public class WearableRepositoryIntegrationTest {
@Autowired
private WearableRepository wearableRepository;
@Test
public void testFindByCriteria() {
assertThat(wearableRepository.findAllByOrderByPriceAscSensorTypeAscPopularityIndexDesc()) .hasSize(4);
}
}
8、运行集成测试 {#8运行集成测试}
在运行集成测试时,我们会发现它无法加载 Application Context,并出现以下错误:
Caused by: java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [price] on this ManagedType [com.baeldung.spring.data.jpa.filtering.WearableEntity]
9、根本原因 {#9根本原因}
Hibernate 使用命名约定将字段映射到数据库列。假设实体类中的字段名与相应的列名或预期约定不一致。在这种情况下,Hibernate 将无法映射它们,从而在执行查询或 Schema 验证时导致异常。
在本例中:
- Hibernate 期望使用
name
、price
或popularityIndex
(驼峰风格)等字段名称,但实体却错误地使用了Id
、Name
、SensorType
、Price
和PopularityIndex
(首字母大写)等字段名称。 - 在执行
findAllByOrderByPriceAsc()
这样的查询时,Hibernate 会尝试将 SQL 价格列映射到实体字段。由于该字段名为Price
(大写字母 "P"),因此无法找到属性,导致出现IllegalArgumentException
异常。
10、修改实体,解决异常 {#10修改实体解决异常}
现在,将 WearableEntity
类中字段的命名从 "首字母大写" 改为 "首字母小写的驼峰" 格式:
@Entity
@Table(name = "wearables")
public class WearableValidEntity {
@Id
@GeneratedValue
private Long id;
@Column(name = "name")
private String name;
@Column(name = "price")
private BigDecimal price;
@Column(name = "sensor_type")
private String sensorType;
@Column(name = "popularity_index")
private Integer popularityIndex;
}
修改完毕后,重新运行 WearableRepositoryIntegrationTest
。测试通过!
11、总结 {#11总结}
本文介绍了Spring JPA 出现 "Unable to Locate Attribute with the Given Name" 异常的原因以及解决办法。强调了遵循 JPA 命名约定和最佳实践的重要性,这有助于避免字段映射问题并优化应用性能。
Ref:https://www.baeldung.com/spring-jpa-troubleshooting-attribute-naming-issues