Spring JPA Criteria with Repository, ManyToOne, Select on joined table 领域

Spring JPA Criteria with Repository, ManyToOne, Select on field of joined table

我正在尝试创建一个 JPA 谓词以用作我的 JPA 存储库中的参数,以便 return 基于连接的 table 中的字段获得结果。它实际上从不过滤任何东西。我得到了主要 table 的所有对象。 JPA 标准似乎是完成这项工作的正确工具,因为实际应用的应用程序有许多不同的条件,并且是一个动态查询。

代码:

@Getter
@Setter
@Entity
@Table(name = "FOO")
public class Foo {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;

    @OneToMany(mappedBy = "foo")
    private Set<Bar> bars;
}

@Getter
@Setter
@Entity
@Table(name = "BAR")
public class Bar {
    @Id
    @Column(name="id")
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private int id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "foo_id", nullable = false)
    private Foo foo;
}


public class FooSpecs {
    public Specification<Foo> barName(String name) {
        return (root, query, builder) -> {
            Root<Foo> foo = query.distinct(true).from(Foo.class);
            Join<Foo, Bar> bar = foo.join("bars", JoinType.LEFT);

            Predicate pred = builder.equal(bar.get("name"), name);

            return pred;
        };
    }
}

public interface FooRepository extends JpaRepository<Foo, Integer>, JpaSpecificationExecutor {
}

测试

@RunWith(SpringRunner.class)
@DataJpaTest
@SpringBootTest
public class JpaDemoApplicationTests {

    @Autowired
    FooRepository repo;

    @Test
    public void contextLoads() {
        FooSpecs specs = new FooSpecs();
        Specification<Foo> spec = specs.barName("test 2");
        List<Foo> foos =  repo.findAll(spec);
        // List<Foo> foos =  repo.findAll();

        assertThat(foos)
                .isNotEmpty()
                .hasSize(1);
    }
}

数据

insert into Foo(id, name) values (1, 'foo 1');
insert into Foo(id, name) values (2, 'foo 2');
insert into Foo(id, name) values (3, 'foo 3');

insert into Bar (id, name, foo_id) values (1, 'test 1', 2);
insert into Bar (id, name, foo_id) values (2, 'test 2', 1);

休眠查询输出:

Hibernate: select distinct foo0_.id as id1_1_, foo0_.name as name2_1_ from foo foo0_ cross join foo foo1_ left outer join bar bars2_ on foo1_.id=bars2_.foo_id where bars2_.name=?

我不是最喜欢连接的人,但交叉连接似乎不合适,根据我的理解,这可能是问题的一部分。

测试输出:

Expected size:<1> but was:<3> in:
<[com.example.jpademo.dto.Foo@6e0e5dec,
    com.example.jpademo.dto.Foo@56476c16,
    com.example.jpademo.dto.Foo@497b560e]>, mergedContextConfiguration = [MergedContextConfiguration@525f1e4e testClass = JpaDemoApplicationTests, locations = '{}', classes = '{class com.example.jpademo.JpaDemoApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@75f9eccc key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, 
java.lang.AssertionError: 
Expected size:<1> but was:<3> in:
<[com.example.jpademo.dto.Foo@6e0e5dec,
    com.example.jpademo.dto.Foo@56476c16,
    com.example.jpademo.dto.Foo@497b560e]>

我期望的结果是 Foo of name 'foo 1'

public class FooSpecs {
    public Specification<Foo> barName(String name) {
        return (root, query, builder) -> {
            Join<Foo, Bar> bar = root.join("bars");
            Predicate pred = builder.equal(bar.get("name"), name);
            return pred;
        };
    }
}