使用Spring Data JPA选择一个列

24

有人知道如何使用Spring Data JPA获取单个列吗?我在我的Spring Boot项目中创建了一个类似下面的仓库,但是每次访问Restful URL时都会收到 {"cause":null,"message":"PersistentEntity must not be null!"} 错误。

@RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UsersRepository extends CrudRepository<Users, Integer> {

    @Query("SELECT u.userName  FROM Users u")
    public List<String> getUserName();
}

如果我访问Restful URL,如../users/search/getUserName,我会收到以下错误: {"cause":null,"message":"PersistentEntity must not be null!"}


3
这对于使用JpaRepository而言对我很有效。 - kervin
6个回答

11
创建一个投影接口。
public interface UserNameOnly {
    String getUserName();
}

然后在您的存储库接口中返回该类型,而不是用户类型。

public interface UserRepository<User> extends JpaRepository<User,String> {
    List<UsernameOnly> findNamesByUserNameNotNull();
}

在投影接口中的get方法必须与JPA存储库上定义类型的get方法匹配,在本例中为User。 "findBySomePropertyOnTheObjectThatIsNotNull"允许您根据某些条件获取实体的列表(而不是可迭代的),对于findAll,这些条件可以简单地是唯一标识符(或任何其他NonNull字段)不为空。


@Alax,您是否考虑接受我的答案? - James Gawron
1
说实话,这不是解决方案,因为投影操作会选择所有字段。 - Boldbayar
我认为James忘记在UserRepository接口中添加@Query了。我已经编辑了@James的代码。希望这将仅返回UserName。 - Muhammad Tariq
我没有。在Spring Data JPA中,查询注释是不必要的,因为它可以解析方法名称以确定要运行的查询。 - James Gawron
1
@Boldbayar,您的说法是不正确的。对于闭合投影,接口上的方法名称与实体上的getter匹配... Hibernate SQL日志确认仅查询接口中存在的字段。开放式投影,其中使用SPEL表达式计算字段值,无法优化,在这种情况下,您是正确的。 - James Gawron

5

概念是:在您的实体类中创建一个仅包含所需实例变量的构造函数。并在下面显示的存储库方法中使用该构造函数。

假设您有一个名为Repository的接口,如下所示

  1. Repository implementation:

    public interface UserRepository<User> extends JpaRepository<User,String>
    {
        @Query(value = "select new com.org.User(usr.userId) from User usr where usr.name(:name)")
        List<User> findUserIdAlone(@Param("name") String user);
    }
    
  2. In Controller

    @RestController
    public class UserController 
    {
        @Autowired
        private UserRepository<User> userRepository; 
    
        @Res
        public ResponseEntity<User> getUser(@PathVariable("usrname") String userName)
        {
            User resultUser = usrRepository.findUserIdAlone(userName);
            return ResponseEntity.ok(resultUser);
        }
    }
    
    public class User 
    {
    
        private String userId,userName;
    
        public User(String userId) 
        {
            this.userId=userId;
        }
        // setter and getters goes here
    }
    

1
在 @Query(value = "select new com.org.User(usr.userId) from User usr where usr.name(:name))") 部分是多余的括号吗? - Azy Sır
@AzySır已将其删除。谢谢。 - Ethiraj

2

这对我来说可行。

public interface UserDataRepository extends JpaRepository<UserData, Long> {

    @Query(value = "SELECT emp_name FROM user_data", nativeQuery = true)
    public List<Object[]> findEmp_name();
}


System.out.println("data"+  userDataRepository.findEmp_name());

上述代码行给出了以下结果:

数据[abhijeet,abhijeet1,abhijeet2,abhijeet3,abhijeet4,abhijeet5]


这似乎无法与自定义对象一起使用。如果您看到 Object[] 的位置,我有一个自定义对象,但它没有起作用。 - Azy Sır

1
如果您只想返回单个列,您应该查看投影和摘录,它将允许您过滤特定的列和其他有用的内容。

0

在Spring Data JPA存储库中,可以提供自定义方法的实现,从而完全控制查询和返回类型。具体步骤如下:

  • 定义一个带有所需方法签名的接口。
  • 实现该接口以实现所需行为。
  • 使存储库同时扩展JpaRepository和自定义接口。

以下是一个使用JpaRepository的工作示例,假设有一个包含两个列user_iduser_nameuser_table

模型包中的UserEntity类:

@Entity
@Table(name = "user_table")
public class UserEntity {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name = "user_id")
    private Long userId;

    @Column(name = "user_name")
    private String userName;

    protected UserEntity() {}

    public UserEntity(String userName) {
    this.userName = userName;

    // standard getters and setters
}

在存储库包中定义自定义存储库的接口:

public interface UserCustomRepository {
    List<String> findUserNames();
}


在存储库包中提供自定义接口的实现类:
public class UserCustomRepositoryImpl implements UserCustomRepository {


    // Spring auto configures a DataSource and JdbcTemplate
    // based on the application.properties file. We can use
    // autowiring to get a reference to it.
    JdbcTemplate jdbcTemplate;

    @Autowired
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    // Now our custom implementation can use the JdbcTemplate
    // to perform JPQL queries and return basic datatypes.
    @Override
    public List<String> findUserNames() throws DataAccessException {
        String sql = "SELECT user_name FROM user_table";
        return jdbcTemplate.queryForList(sql, String.class);
    }
}

最后,我们只需要让 UserRepository 同时扩展 JpaRepository 和我们刚刚实现的自定义接口即可。
public interface UserRepository extends JpaRepository<UserEntity, Long>, UserCustomRepository {}

使用JUnit 5编写的简单测试类(假设数据库最初为空):

@SpringBootTest
class UserRepositoryTest {

    private static final String JANE = "Jane";
    private static final String JOE = "Joe";

    @Autowired
    UserRepository repo;

    @Test
    void shouldFindUserNames() {
        UserEntity jane = new UserEntity(JANE);
        UserEntity joe = new UserEntity(JOE);

        repo.saveAndFlush(jane);
        repo.saveAndFlush(joe);

        List<UserEntity> users = repo.findAll();
        assertEquals(2, users.size());

        List<String> names = repo.findUserNames();
        assertEquals(2, names.size());
        assertTrue(names.contains(JANE));
        assertTrue(names.contains(JOE));
    }

}

0

非常感谢您的回答。实际上,它仍然无法正常工作。我得到了相同的错误。如果列出所有列,则可以正常工作,但如果只列出其中一部分,则无法正常工作。 - Alax

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接