针对H2的Spring数据JPA查询失败,找不到架构

时间:2018-07-18 21:54:44

标签: hibernate spring-data-jpa h2

我有一个名为user的实体,如下所示:

    @Entity
    @JsonSerialize(using = UserSerializer.class)
    @Table(uniqueConstraints={@UniqueConstraint(columnNames = {"username"})})
    public class User implements UserDetails {

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;

        @Column(unique = true, updatable = false)
        private String username;

        private String password;

        @OneToOne(cascade={CascadeType.ALL})
        @JoinColumn(name = "person_id")
        private Person person;

        @OneToOne(cascade={CascadeType.PERSIST})
        @JoinColumn(name = "maintainer_id")
        private Maintainer maintainer;

        @ManyToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE}, fetch = 
 FetchType.EAGER)
        @JoinTable(name = "user_role",
                joinColumns = @JoinColumn(name = "user_id",
                        nullable = false, updatable = false),
                inverseJoinColumns = @JoinColumn(name = "role_id",
                        nullable = false, updatable = false))
        private Set<Role> roles;

        public User() {
        }

        public User(String username, String password, Person person, Maintainer maintainer, Set<Role> roles) {
            this.username = username;
            this.password = password;
            this.person = person;
            this.maintainer = maintainer;
            this.roles = roles;
        }

        // builder inner class omitted 

        // getters and setters omitted

        // equals, hashCode and toString omitted    
    }

我还有一个用户摘要类,旨在将其返回以进行分页和排序:

public class UserRow {
    private Long id;
    private String username;
    private String firstName;
    private String lastName;
    private Set<Role> roles;
    private String role;
    private Long maintainerId;
    private String maintainerName;

    public static UserRow of(Long id, String username, String firstName, String lastName, Set<Role> roles, Long maintainerId, String maintainerName) {
        UserRow userRow = new UserRow();

        userRow.id = id;
        userRow.username = username;
        userRow.firstName = firstName;
        userRow.lastName = lastName;
        userRow.roles = roles;
        userRow.maintainerId = maintainerId;
        userRow.maintainerName = maintainerName;


        return userRow;
    }

// getters, setters, equals, hashCode and toString omitted
}

这与所有其他实体一起似乎在h2中很好地初始化了,从控制台中我看到了类似的东西:

create table user (
   id bigint generated by default as identity,
    password varchar(255),
    username varchar(255),
    maintainer_id bigint,
    person_id bigint,
    primary key (id)
)

create table user_role (
   user_id bigint not null,
    role_id bigint not null,
    primary key (user_id, role_id)
)

甚至:

alter table user 
   add constraint UK_sb8bbouer5wak8vyiiy4pf2bx unique (username)

现在,我已经设置了控制器,服务和存储库...

控制器:

@Secured("ROLE_ADMIN")
@GetMapping(value = {"/users", "/users/maintainers/{mid}"}, produces = "application/json")
@ResponseStatus(value = HttpStatus.OK)
Response<List<User>> getPagedUsers(
        @PathVariable("mid") Optional<Long> maintainerId,
        @PageableDefault(page = DEFAULT_PAGE_NUMBER, size = DEFAULT_PAGE_SIZE)
        @SortDefault.SortDefaults({
                @SortDefault(sort = "username", direction = Sort.Direction.ASC),
                @SortDefault(sort = "p.lastName", direction = Sort.Direction.ASC)
        }) Pageable pageable) {

    Page<UserRow> users;
    if (maintainerId.isPresent()) {
        users = userService.findSortedSummaryByMaintainer(maintainerId.get(), pageable);
    } else {
        users = userService.findSortedSummary(pageable);
    }

    return Response.of(users);
}

服务:     公共接口UserService扩展了UserDetailsS​​ervice {

    Page<UserRow> findSortedSummary(Pageable pageable);

    @Service
    class UserServiceImpl implements UserService {

        private static final Logger LOG = LoggerFactory.getLogger(UserService.class);

        @Autowired
        BCryptPasswordEncoder bCryptPasswordEncoder;

        @Autowired
        UserRepository userRepository;

        @Autowired
        RoleRepository roleRepository;

        public Page<UserRow> findSortedSummary(Pageable pageable) {

            Page<UserRow> userPage = userRepository.findSortedSummary(pageable.next());

            userPage.forEach(us -> us.setRole(findMostPrivilegedRole(us.getRoles())));

            return userPage;
        }

    }

}

和存储库:     公共接口UserRepository扩展了JpaRepository {

    @Query(value = "select u.username, p.firstName, p.lastName, u.roles, m.id, m.name "
                    + "from User u "
                    + "inner join u.person p "
                    + "inner join u.maintainer m",
            countQuery = "select count(*) "
                    + "from User u "
                    + "inner join u.person p "
                    + "inner join u.maintainer m",
            nativeQuery = true)
    Page<UserRow> findSortedSummary(Pageable pageable);

}

最后,我正在用以下方法测试所有这些:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("embedded")
@AutoConfigureMockMvc
public class UserListControllerFunctionalTest {

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private MockMvc mvc;

    @Autowired
    UserService userService;

    List<User> franks;

    @Before
    public void setup() {
        mvc = MockMvcBuilders
                .webAppContextSetup(context)
                .apply(springSecurity())
                .build();

        franks = ModelFixtures.createFrankAndCohorts();
        franks = userService.createAll(franks);
    }

    @Test
    public void getListOfUsersWithRootSuccess() throws Exception {

        mvc.perform(get("/api/users").header(AUTHORIZATION_HEADER, "Bearer " + ModelFixtures.ROOT_JWT_TOKEN))
                .andDo(print())
                .andExpect(status().isOk());

    }

    @After
    public void tearDown() {
        userService.deleteAll(franks);
    }

}

所有的spring security东西都经过了广泛的测试并可以正常工作。

测试调用控制器,服务和存储库时,我在存储库中遇到以下故障:

  

由以下原因引起:org.h2.jdbc.JdbcSQLException:找不到架构“ U”;的SQL   语句:选择u.username,p.firstName,p.lastName,u.roles,m.id,   用户的m。名称u内部联接u.person p内部联接u.maintainer m   按u.username asc,p.lastName asc限制排序?抵消? [90079-197]     在   org.h2.message.DbException.getJdbcSQLException(DbException.java:357)     在org.h2.message.DbException.get(DbException.java:179)处   org.h2.message.DbException.get(DbException.java:155)

据我到目前为止发现,别名表如“ User”和字母如“ u”都很好,h2(和MySQL)中用户的内部表是“ users”,因此“ User”未保留。所以我看不出问题出在哪里。有人可以帮忙吗?

相关依赖项:

|    +--- org.hibernate:hibernate-core:5.2.17.Final
|    |    +--- org.jboss.logging:jboss-logging:3.3.1.Final -> 3.3.2.Final
|    |    +--- org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final
|    |    +--- org.javassist:javassist:3.22.0-GA
|    |    +--- antlr:antlr:2.7.7
|    |    +--- org.jboss:jandex:2.0.3.Final
|    |    +--- com.fasterxml:classmate:1.3.0 -> 1.3.4
|    |    +--- dom4j:dom4j:1.6.1
|    |    \--- org.hibernate.common:hibernate-commons-annotations:5.0.1.Final
|    |         \--- org.jboss.logging:jboss-logging:3.3.0.Final -> 3.3.2.Final
|    +--- javax.transaction:javax.transaction-api:1.2
|    +--- org.springframework.data:spring-data-jpa:2.0.7.RELEASE
|    |    +--- org.springframework.data:spring-data-commons:2.0.7.RELEASE
|    |    |    +--- org.springframework:spring-core:5.0.6.RELEASE (*)
|    |    |    +--- org.springframework:spring-beans:5.0.6.RELEASE (*)
|    |    |    \--- org.slf4j:slf4j-api:1.7.25
|    |    +--- org.springframework:spring-orm:5.0.6.RELEASE
|    |    |    +--- org.springframework:spring-beans:5.0.6.RELEASE (*)
|    |    |    +--- org.springframework:spring-core:5.0.6.RELEASE (*)
|    |    |    +--- org.springframework:spring-jdbc:5.0.6.RELEASE (*)
|    |    |    \--- org.springframework:spring-tx:5.0.6.RELEASE (*)
|    |    +--- org.springframework:spring-context:5.0.6.RELEASE (*)
|    |    +--- org.springframework:spring-aop:5.0.6.RELEASE (*)
|    |    +--- org.springframework:spring-tx:5.0.6.RELEASE (*)
|    |    +--- org.springframework:spring-beans:5.0.6.RELEASE (*)
|    |    +--- org.springframework:spring-core:5.0.6.RELEASE (*)
|    |    \--- org.slf4j:slf4j-api:1.7.25

编辑-删除了nativeQuery = true部分,解决了“找不到架构”问题-本机查询必须意味着按字面意义对待所有元素。

然而问题却变成了:

Caused by: 

org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "

SELECT 
USER0_.USERNAME AS COL_0_0_, 
PERSON1_.FIRST_NAME AS COL_1_0_, 
PERSON1_.LAST_NAME AS COL_2_0_, 
.[*] AS COL_3_0_, 
MAINTAINER2_.ID AS COL_4_0_, 
MAINTAINER2_.NAME AS COL_5_0_, 
ROLE4_.ID AS ID1_17_, 
ROLE4_.DESCRIPTION AS DESCRIPT2_17_, 
ROLE4_.ROLE_NAME AS ROLE_NAM3_17_ 
FROM USER USER0_ 
INNER JOIN PERSON PERSON1_ ON USER0_.PERSON_ID=PERSON1_.ID 
INNER JOIN MAINTAINER MAINTAINER2_ ON USER0_.MAINTAINER_ID=MAINTAINER2_.ID 
INNER JOIN USER_ROLE ROLES3_ ON USER0_.ID=ROLES3_.USER_ID 
INNER JOIN ROLE ROLE4_ ON ROLES3_.ROLE_ID=ROLE4_.ID 
ORDER BY USER0_.USERNAME ASC, PERSON1_.LAST_NAME ASC LIMIT ? OFFSET ? "; 

expected "*, NOT, EXISTS, INTERSECTS, SELECT, FROM, WITH"; 

不确定在这里预期的“预期”事情在哪里...

1 个答案:

答案 0 :(得分:0)

好,我已经解决了。

一切都在查询构造中。因此,正确执行jpql的方法是:

@Query(value = "select new au.com.avmaint.api.access.model.UserRow(u.id, u.username, p.firstName, p.lastName, m.id, m.name) "
                + "from User u "
                + "inner join u.person p "
                + "inner join u.maintainer m",
        countQuery = "select count(*) "
                + "from User u "
                + "inner join u.person p "
                + "inner join u.maintainer m")
Page<UserRow> findSortedSummary(Pageable pageable);

如果打算返回非映射类(UserRow),则需要使用构造函数表示法。您也不能在SELECT子句中返回集合(user.roles),因此我不得不在另一个查询中获得角色。这很容易在服务层中完成(请参见forEach块的第一行):

    public Page<UserRow> findSortedSummary(Pageable pageable) {

        Page<UserRow> userPage = userRepository.findSortedSummary(pageable.next());

        userPage.forEach(row -> {
            User user = userRepository.findById(row.getId()).orElseThrow(() -> new UsernameNotFoundException("User not found: " + row.getUsername()));
            row.setRoles(user.getRoles());
            row.setRole(findMostPrivilegedRole(user.getRoles()));
        });

        return userPage;
    }

在这里,我使用了标准的Repository findById调用,以按ID返回单个用户并以这种方式获取角色。在单个查询中获得角色当然会很好,但是JPQL只是不具备该功能。

最后我可能会说,除了最琐碎的域示例(基本上是被博客的示例)外,对其他任何内容的分页和排序实际上都是非常棘手和复杂的。一旦我真的把这些东西整理好了,我就会在这个博客上写一些实际上有用的东西。