Spring JPA 屬性命名問題故障排除
一、簡介
Spring 為程式設計師提供的簡化 Java 應用程式中的資料庫互動的最強大的框架之一是 Spring JPA(Java Persistence API)。它提供了對 JPA 的可靠抽象。
然而,儘管易於使用,但開發人員經常遇到難以診斷和解決的錯誤。其中一個常見問題是「無法使用給定名稱來尋找屬性」錯誤。
在本教程中,我們先檢查此問題的根源,然後再研究如何修復它。
2. 定義用例
有一個實際的用例來說明本文總是有幫助的。
我們創造獨特且引人注目的可穿戴設備。經過最近的一項調查,我們的行銷團隊發現,在我們的平台上按感測器類型、價格和受歡迎程度對產品進行排序可以突出顯示最受歡迎的商品,從而幫助客戶做出更好的購買決策。
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.新增應用資源
為了測試這個儲存庫,讓我們建立一些應用程式屬性條目,幫助我們建立名為 WEARABLES 的表並將其填入 H2 記憶體資料庫中。
在我們應用程式的main/resources資料夾中,讓我們建立一個包含以下條目的application-h2.properties :
# H2 configuration
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
我們將放置在 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. 定義穿戴式實體模型
讓我們定義我們的實體模型WearableEntity 。該模型是我們定義穿戴式裝置特徵的實體:
@Entity
public class WearableEntity {
@Id @GeneratedValue
private Long Id;
@Column(name = "name")
private String Name;
@Column(name = "price")
private BigDecimal Price;
// eg, "Heart Rate Monitor", "Neuro Feedback", etc.
@Column(name = "sensor_type")
private String SensorType;
@Column(name = "popularity_index")
private Integer PopularityIndex;
}
6. 定義實體過濾查詢
在平台中引入上述實體後,讓我們在資料庫中新增一個查詢,使我們的客戶能夠使用 Spring JPA 框架根據持久層中的新過濾條件來過濾WearableEntity, 。
public interface WearableRepository extends JpaRepository<WearableEntity, Long> {
List<WearableEntity> findAllByOrderByPriceAscSensorTypeAscPopularityIndexDesc();
}
讓我們分解上面的查詢以更好地理解它。
-
findAllBy:讓我們使用此方法來擷取所有屬於或類型為WearableEntity記錄 -
OrderByPriceAsc:讓我們按價格升序對結果進行排序 -
SensorTypeAsc:依價格排序後,讓我們按sensorType升序排序 -
PopularityIndexDesc:最後,讓我們按popularityIndex降序對結果進行排序(因為可能會優先考慮較高的流行度)
7. 透過整合測試測試儲存庫
現在讓我們透過在專案中引入整合測試來檢查WearableRepository的行為:
public class WearableRepositoryIntegrationTest {
@Autowired
private WearableRepository wearableRepository;
@Test
public void testFindByCriteria() {
assertThat(wearableRepository.findAllByOrderByPriceAscSensorTypeAscPopularityIndexDesc()) .hasSize(4);
}
}
8. 運行整合測試
但在執行整合測試時,我們會立即註意到它無法載入應用程式上下文並失敗並出現以下錯誤:
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. 了解根本原因
Hibernate 使用命名約定將欄位對應到資料庫列。假設實體類別中的欄位名稱與對應的欄位名稱或預期約定不一致。在這種情況下,Hibernate 將無法對應它們,導致查詢執行或模式驗證期間出現異常。
在這個例子中:
- Hibernate 需要諸如
name、price或popularityIndex(以駝峰命名法)之類的欄位名稱,但實體錯誤地使用了欄位名稱Id, Name, SensorType, Price和PopularityIndex(以 PascalCase 命名) - 當執行像
findAllByOrderByPriceAsc()這樣的查詢時,Hibernate 將嘗試將 SQL價格列對應到實體欄位。由於該欄位名為Price(帶有大寫“P”),因此無法找到該屬性,從而導致IllegalArgumentException
10. 透過修復實體來解決錯誤
現在讓我們將WearableEntity類別中欄位的命名從 PascalCase 更改為 CamelCase:
@Entity
@Table(name = "wearables")
public class WearableValidEntity {
@Id
@GeneratedValue
private Long id;
@Column(name = "name")
// use camelCase instead of PascalCase
private String name;
@Column(name = "price")
// use camelCase instead of PascalCase
private BigDecimal price;
@Column(name = "sensor_type")
// use camelCase instead of PascalCase
private String sensorType;
@Column(name = "popularity_index")
// use camelCase instead of PascalCase
private Integer popularityIndex;
}
完成此變更後,讓我們重新執行WearableRepositoryIntegrationTest 。瞧!它立刻就過去了。
11. 結論
在本文中,我們強調了遵循 JPA 命名約定對於防止執行時間錯誤並確保順利的資料互動的重要性。遵循最佳實踐有助於避免現場映射問題並優化應用程式效能。
像往常一樣,我們可以在 GitHub 上找到完整的程式碼範例。