Uczę sie Spring Data Jpa i stworzyłem prosty schemat bazy danych jak na obrazku poniżej:
Wszystkie 3 tabele są w relacjach wiele do wielu.
Gdy wstawiam poszczególne encje a nawet gdy je łączą to wszystko jest w porządku. Ale problem pojawia się gdy chce wyszukać wszystkie przedmioty, konkretnego studenta, które odbywają się w konkretnej sali wykładowej.
Korzystając ze Spring Data Jpa myślałem, że da się to napisać w taki sposób:
ModuleRepository.java
public interface ModuleRepository extends JpaRepository<Module, String> {
List<Module> findAllByStudentsAndHalls(List<Student> students, List<Hall> halls);
}
DemoApplication.java
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public CommandLineRunner mappingDemo(HallRepository hallRepository,
ModuleRepository moduleRepository,
StudentRepository studentRepository) {
return args -> {
Student student = new Student("Jan", LocalDate.of(2000, 1 ,1));
studentRepository.save(student);
Hall hall = new Hall(120);
hallRepository.save(hall);
Module module = new Module("12345", "Algebra");
moduleRepository.save(module);
module.getStudents().add(student);
module.getHalls().add(hall);
moduleRepository.save(module);
// do tego miejsca wszystko jest ok. Encje są zapisywane w bazie danych
List<Module> modules = moduleRepository.findAllByStudentsAndHalls(Arrays.asList(student), Arrays.asList(hall));
LOGGER.info(modules.toString());
};
}
private static final Logger LOGGER = LoggerFactory.getLogger(DemoApplication.class);
}
Niestety próba wyszukania wszystkich przedmiotów, konkretnego studenta, które odbywają się w konkretnej sali wykładowej kończy się wyjątkiem:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-06-16 14:41:09.906 ERROR 13120 --- [ main] o.s.boot.SpringApplication : Application run failedorg.springframework.beans.factory.BeanCreationException: Error creating bean with name 'moduleRepository': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.List pl.kkk.demo.repositories.ModuleRepository.findAllByStudentsAndHalls(java.util.List,java.util.List)! Operator SIMPLE_PROPERTY on students requires a scalar argument, found interface java.util.List in method public abstract java.util.List pl.kkk.demo.repositories.ModuleRepository.findAllByStudentsAndHalls(java.util.List,java.util.List).
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:178) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:101) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1821) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getObjectForBeanInstance(AbstractAutowireCapableBeanFactory.java:1266) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:621) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:609) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.data.repository.config.DeferredRepositoryInitializationListener.onApplicationEvent(DeferredRepositoryInitializationListener.java:51) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.repository.config.DeferredRepositoryInitializationListener.onApplicationEvent(DeferredRepositoryInitializationListener.java:36) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:404) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:361) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:898) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:554) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at pl.kkk.demo.DemoApplication.main(DemoApplication.java:25) ~[main/:na]
Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.List pl.kkk.demo.repositories.ModuleRepository.findAllByStudentsAndHalls(java.util.List,java.util.List)! Operator SIMPLE_PROPERTY on students requires a scalar argument, found interface java.util.List in method public abstract java.util.List pl.kkk.demo.repositories.ModuleRepository.findAllByStudentsAndHalls(java.util.List,java.util.List).
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:96) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:107) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:218) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:81) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lookupQuery(QueryExecutorMethodInterceptor.java:99) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lambda$mapMethodsToQuery$1(QueryExecutorMethodInterceptor.java:92) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) ~[na:na]
at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133) ~[na:na]
at java.base/java.util.Collections$UnmodifiableCollection$1.forEachRemaining(Collections.java:1056) ~[na:na]
at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[na:na]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.mapMethodsToQuery(QueryExecutorMethodInterceptor.java:94) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lambda$new$0(QueryExecutorMethodInterceptor.java:84) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at java.base/java.util.Optional.map(Optional.java:258) ~[na:na]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.<init>(QueryExecutorMethodInterceptor.java:84) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:331) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:297) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.util.Lazy.getNullable(Lazy.java:212) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.util.Lazy.get(Lazy.java:94) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:244) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:57) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:171) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
... 23 common frames omitted
Caused by: java.lang.IllegalStateException: Operator SIMPLE_PROPERTY on students requires a scalar argument, found interface java.util.List in method public abstract java.util.List pl.kkk.demo.repositories.ModuleRepository.findAllByStudentsAndHalls(java.util.List,java.util.List).
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.throwExceptionOnArgumentMismatch(PartTreeJpaQuery.java:171) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.validate(PartTreeJpaQuery.java:147) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:90) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
... 48 common frames omitted
Załączam jeszcze plik Module.java gdyby ktoś zechciał w niego spojrzeć (pominąłem importy, gettery, settery, etc.):
@Entity
public class Module {
@Id
@Column(length = 5, nullable = false)
private String id;
@Column(length = 40, nullable = false)
private String name;
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinTable(
name = "rel_module_student",
joinColumns = {
@JoinColumn(
name = "module_id",
referencedColumnName = "id",
nullable = false,
updatable = false)},
inverseJoinColumns = {
@JoinColumn(
name = "student_id",
referencedColumnName = "id",
nullable = false,
updatable = false)}
)
List<Student> students = new ArrayList<>();
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinTable(
name = "rel_module_hall",
joinColumns = {
@JoinColumn(
name = "module_id",
referencedColumnName = "id",
nullable = false,
updatable = false)},
inverseJoinColumns = {
@JoinColumn(
name = "hall_id",
referencedColumnName = "id",
nullable = false,
updatable = false)}
)
List<Hall> halls = new ArrayList<>();
}
Jak to naprawić by działało? Źle nazwałem metodę w interfejsie ModuleRepository? Jeśli tak to jak powinna się nazywać? Przykład jest tak prosty, że zakładam, że nie potrzeba tutaj kombinować z JPQL czy zapytaniami natywnymi.