我有一个带有枚举的实体类,如下所示
package com.expensetracker.api.domain;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.CreatedDate;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Entity
@Table(name = "transaction")
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ExpenseTransaction {
public enum TransactionType {
INCOME,
EXPENSE,
INVESTMENT
}
public enum Category {
FOOD,
RENT,
TRANSPORTATION,
ENTERTAINMENT,
GROCERIES,
SHOPPING,
MUTUAL_FUNDS,
STOCKS,
OTHER_INVESTMENTS,
LOAN,
SALARY,
OTHER
}
@Id
@SequenceGenerator(name = "ex_trn_seq_gen",sequenceName = "ex_trn_seq")
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "ex_trn_seq_gen")
private Long id;
@Column(nullable = false)
private BigDecimal amount;
@Column(name = "transaction_date",nullable = false)
private LocalDate transactionDate;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private TransactionType type;
@Enumerated(EnumType.STRING)
private Category category;
@Column(nullable = false)
private String description;
@CreatedDate
@Column(nullable = false)
private LocalDateTime createdAt;
}
我的DTO课程如下
package com.expensetracker.api.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ExpenseTransactionDTO {
private Long id;
private BigDecimal amount;
private LocalDate transactionDate;
private ExpenseTransaction.TransactionType type;
private ExpenseTransaction.Category category;
private String description;
private LocalDateTime createdAt;
}
我正在尝试在 Spring JPARepository 中使用简单的 JPA 查询编写 DTO 投影选择查询,如下所示:
@Query("SELECT new com.expensetracker.api.domain.ExpenseTransactionDTO(e.id,e.amount,e.transactionDate,e.type,e.category,e.description,e.createdAt) from ExpenseTransaction e")
Page<ExpenseTransactionDTO> findExpenseTransactions(Pageable pageable);
当我运行此方法时,出现以下异常
2024-03-24T22:43:03.382+05:30 ERROR 25232 --- [expensetracker-api] [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.InvalidDataAccessApiUsageException: Cannot instantiate class 'com.expensetracker.api.domain.ExpenseTransactionDTO' (it has no constructor with signature [java.lang.Long, java.math.BigDecimal, java.time.LocalDate, com.expensetracker.api.domain.ExpenseTransaction$TransactionType, com.expensetracker.api.domain.ExpenseTransaction$Category, java.lang.String, java.time.LocalDateTime], and not every argument has an alias)] with root cause
java.lang.IllegalStateException: Cannot instantiate class 'com.expensetracker.api.domain.ExpenseTransactionDTO' (it has no constructor with signature [java.lang.Long, java.math.BigDecimal, java.time.LocalDate, com.expensetracker.api.domain.ExpenseTransaction$TransactionType, com.expensetracker.api.domain.ExpenseTransaction$Category, java.lang.String, java.time.LocalDateTime], and not every argument has an alias)
at org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiationResultImpl.resolveAssembler(DynamicInstantiationResultImpl.java:201) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiationResultImpl.createResultAssembler(DynamicInstantiationResultImpl.java:107) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.sql.results.jdbc.internal.StandardJdbcValuesMapping.resolveAssemblers(StandardJdbcValuesMapping.java:53) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.sql.results.internal.ResultsHelper.createRowReader(ResultsHelper.java:77) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.sql.results.internal.ResultsHelper.createRowReader(ResultsHelper.java:62) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:188) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:83) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:76) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:65) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$2(ConcreteSqmSelectQueryPlan.java:137) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:362) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:303) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:509) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:427) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.hibernate.query.Query.getResultList(Query.java:120) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
at org.springframework.data.jpa.repository.query.JpaQueryExecution$PagedExecution.doExecute(JpaQueryExecution.java:204) ~[spring-data-jpa-3.2.4.jar:3.2.4]
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:92) ~[spring-data-jpa-3.2.4.jar:3.2.4]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:149) ~[spring-data-jpa-3.2.4.jar:3.2.4]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:137) ~[spring-data-jpa-3.2.4.jar:3.2.4]
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170) ~[spring-data-commons-3.2.4.jar:3.2.4]
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158) ~[spring-data-commons-3.2.4.jar:3.2.4]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164) ~[spring-data-commons-3.2.4.jar:3.2.4]
这是因为我的 DTO 类已将枚举属性定义为字符串数据类型而不是枚举。我想在我的 DTO 中使用 String。我应该如何编写 JPA 查询,以便它映射到字符串而不是枚举?
@Repository
public interface ExpenseTransactionRepository extends JpaRepository<ExpenseTransaction, Long> {
@Query(nativeQuery = true,
value = "SELECT t.id, t.amount, t.transaction_date, t.type, t.category, t.description, t.created_at FROM transaction AS t")
List<TestInterface> findExpenseTransactionsNative();
}
public interface TestInterface {
Long getId();
BigDecimal getAmount();
LocalDate getTransactionDate();
String getType();
String getCategory();
String getDescription();
LocalDateTime getCreatedAt();
}
@SpringBootTest
public class QueryTest {
@Autowired
ExpenseTransactionRepository expenseTransactionRepository;
@Test
void test() {
ExpenseTransaction example = new ExpenseTransaction(1L, BigDecimal.ZERO, LocalDate.now(),
ExpenseTransaction.TransactionType.EXPENSE, ExpenseTransaction.Category.RENT, "example",
LocalDateTime.now());
expenseTransactionRepository.save(example);
List<TestInterface> expenseTransactionsNative = expenseTransactionRepository.findExpenseTransactionsNative();
TestInterface first = expenseTransactionsNative.getFirst();
Assertions.assertThat(first.getType()).isEqualTo("EXPENSE");
}
}
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ExpenseTransactionDTO {
private Long id;
private BigDecimal amount;
private LocalDate transactionDate;
private TransactionType type;
private Category category;
private String description;
private LocalDateTime createdAt;
}
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ExpenseTransactionResponse {
private Long id;
private BigDecimal amount;
private LocalDate transactionDate;
private String type;
private String category;
private String description;
private LocalDateTime createdAt;
public static ExpenseTransactionResponse from(ExpenseTransactionDTO dto) {
return new ExpenseTransactionResponse(
dto.getId(),
dto.getAmount(),
dto.getTransactionDate(),
dto.getType().name(),
dto.getCategory().name(),
dto.getDescription(),
dto.getCreatedAt()
);
}
}
@Repository
public interface ExpenseTransactionRepository extends JpaRepository<ExpenseTransaction, Long> {
@Query("SELECT new com.example.demo.dto.ExpenseTransactionDTO(e.id,e.amount,e.transactionDate,e.type,e.category,e.description,e.createdAt) "
+ "from ExpenseTransaction e")
Page<ExpenseTransactionDTO> findExpenseTransactions(Pageable pageable);
}
@SpringBootTest
public class QueryTest {
@Autowired
ExpenseTransactionRepository expenseTransactionRepository;
@Test
void test() {
ExpenseTransaction example = new ExpenseTransaction(1L, BigDecimal.ZERO, LocalDate.now(),
ExpenseTransaction.TransactionType.EXPENSE, ExpenseTransaction.Category.RENT, "example",
LocalDateTime.now());
expenseTransactionRepository.save(example);
PageRequest pageRequest = PageRequest.of(0, 5);
Page<ExpenseTransactionDTO> expenseTransactions = expenseTransactionRepository.findExpenseTransactions(
pageRequest);
List<ExpenseTransactionResponse> collect = expenseTransactions.getContent()
.stream()
.map(ExpenseTransactionResponse::from)
.toList();
Assertions.assertThat(collect.getFirst().getType()).isEqualTo("EXPENSE");
}
}
希望对您有所帮助。谢谢你:)