I'm new to Spring WebFlux reactive. I use R2DBC postgresql. I have a repository like that:
public interface BookRepository extends ReactiveCrudRepository<Book, Long> {
}
Now I want to add custom method to query by many complicated conditions:
public interface CustomBookRepository extends BookRepository {
Flux<Book> findByVeryComplicatedCondition(MyCriteriaDto criteria);
}
My implementation:
public class CustomBookRepositoryImpl extends CustomBookRepository {
//How to get it?
EntityManager em;
#Override
public Flux<Book> findByVeryComplicatedCondition(MyCriteriaDto criteria) {
Query query = em.createQuery("SELECT b from Book b WHERE (VERY COMPLICATED CONDITIONS)");
//What next?
}
}
My questions are in the code above:
How to obtain an EntityManager?
How to get Flux from HQL query I built?
When I ask these questions, I mean "How to do this with Spring reactive/r2dbc way", not "How to do this normal way with JDBC"
Spring Data R2DBC is not backed by a fully-fledged ORM framework like Hibernate. Therefore there are no such things as EntityManager and no possibility to write JQL/HQL queries.
However, one can still use native queries to define more complex methods, e.g.
interface MyRepository extends ReactiveCrudRepository<...> {
#Query("SELECT b from Book b WHERE (VERY COMPLICATED CONDITIONS)")
Flux<...> find(...);
}
If you want to use Hibernate/JPA JPQL like query in your project, consider Hibernate Reactive.
I have written an article to describe how to use Hibernate Reactive with Spring (BTW, I have contributed a patch to register SmallRye in Spring framework, so at the end of this article, you can skip to register MutinyAdapter in ReactiveAdapterRegistry ).
Spring Data has no plan to integrate Hibernate Reactive, so there is no Repository support.
Hibernate Reactive depends on Vertx reactive drivers, it does not support R2dbc.
HibernateReactive supports another ReactiveStreams implemetnation from Redhat - Smallrye Munity.
Related
I'm implementing a project using SpringBoot, JPA and Hibernate.
I implemented the DB entities layer with JPA repository.
I'm interested to understand the best practice to write unit-tests for this layer.
Point number one: for this layer, from your point of view, it's necessary to use an integrated DB or it' necessary to mock using, for example, Mockito?
My idea, for this layer, it's, for example, to test the entity structure: check fields validation for example, insert and retrieve some data. In this way, I think I could cover the tests for this entire data-layer.
I'm trying to understand these best practices and, in the mean time, I tried to write a first example of the test:
#ActiveProfiles("test")
#DisplayName("Test Item JPA Entity")
#DataJpaTest
#AutoConfigureTestDatabase( replace = AutoConfigureTestDatabase.Replace.NONE )
public class ItemEntityTest {
#Autowired
MyEntityRepository repo;
#Test
#Transactional
public void testEntityCreation() {
Entity e = new Entity();
e.setMyField1("A");
e.setMyField1("A");
//e.setMandatoryField("C")
repo.save(e);
}
}
Unfortunately, In this case, I notiest that the fields validation is not applied (#NotNull or #NotEmpty, or #Column(nullable=false), etc ... If I try to save the entity into my application the validation works fine... the exceptions are raised). Why?
Also some "automatic fields" (for example creation time and last modification time) are not filled.
Is this the correct path? Ho to test my entities definition?
As mentioned in that answer, the problem is that with #DataJpaTest spring will use TestEntityManager and the transactional annotation will override the default auto commit and rollback behaviour by spring boot.
So your test method will pass and assuming the hibernate is the ORM being used here, when the flushing will take place finally, hibernate's pre-insert event will fire and validations will be applied, but your test case will pass till then so it will produce false positive (as the terms used in spring docs)
Solution: You would need to inject entity manager in your test and flush manually it so that hibernate pre-insert event triggers before your test completes.
#ActiveProfiles("test")
#DisplayName("Test Item JPA Entity")
#DataJpaTest
#AutoConfigureTestDatabase( replace = AutoConfigureTestDatabase.Replace.NONE )
public class ItemEntityTest {
#Autowired
MyEntityRepository repo;
#Autowired
private TestEntityManager em;
#Test
#Transactional
public void testEntityCreation() {
Entity e = new Entity();
e.setMyField1("A");
e.setMyField1("A");
//e.setMandatoryField("C")
repo.save(e);
em.flush();
}
}
This must trigger your validations on the entity applied, this is documented in spring framework docs.
Please notice that the documentation is about spring framework and uses session factory, but the concept is same
You may check the spring boot docs as well, which points to the spring framework docs for this behaviour.
This question already has answers here:
How are Spring Data repositories actually implemented?
(1 answer)
How does Spring Data JPA work internally
(1 answer)
Closed 3 years ago.
I have a code like this:
Repository
#Repository
public interface EquipmentRepository extends JpaRepository<Equipment, Integer>{
Equipment findById(int id);
}
Service
#Service
public class EquipmentServiceImpl implements EquipmentService {
#Autowired
EquipmentRepository equipmentRepository;
#Override
public Equipment findById(int id) {
return equipmentRepository.findById(id);
}
}
I wonder that why i can call a method of 'interface EquipmentRepository'. EquipmentRepository is a interface, Right ?
Spring Repository is responsible for importing the DAO's into the DI container and also it makes the unchecked exceptions into Spring DataAccessException. The Spring Repository annotation is meta annotated with the #Component annotation so that the repository classes will be taken up for component scanning.
Teams implementing traditional Java EE patterns such as "Data Access
Object" may also apply this stereotype to DAO classes, though care
should be taken to understand the distinction between Data Access
Object and DDD-style repositories before doing so. This annotation is
a general-purpose stereotype and individual teams may narrow their
semantics and use as appropriate.
A class thus annotated is eligible for Spring DataAccessException
translation when used in conjunction with a
PersistenceExceptionTranslationPostProcessor. The annotated class is
also clarified as to its role in the overall application architecture
for the purpose of tooling, aspects, etc.
Source: JavaDoc
but in your case you are also extending the JpaRepository of Spring Data JPA. Spring Data automatically provides implementations of common CRUD operations. The JpaRepository extends the interface CrudRepository which has the methods declared for all basic crud operations.
public interface EquipmentRepository extends JpaRepository<Account, Long> { … }
Defining this interface serves two purposes:
First, by extending JpaRepository we get a bunch of generic CRUD
methods into our type that allows saving Equipments, deleting them and
so on.
Second, this will allow the Spring Data JPA repository infrastructure
to scan the classpath for this interface and create a Spring bean for
it.
The #EnableJpaRepositories scans all packages below com.acme.repositories for interfaces extending JpaRepository and creates a Spring bean for it that is backed by an implementation of SimpleJpaRepository (spring data provides default imlpementations of CRUD repository through this class).
So that is why even when you haven't defined the method , you are able to do crud operations through this setup.
Refer : https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.repositories
I'm developing a web application using MVC architectural pattern.
Struts2 (version 2.3.24) is used for the Business Logic and Presentation Layer
Spring (version 4.1.0) is the dependency injection engine
Hibernate (version 3.6.10) is used for the Data Layer.
I have to create a PaginationFactory class that I can dynamically use for the various section of the application. I've several examples on google and StackOverflow... But mostly old stuff like this question.
Any ideas on how implement this function with using something more modern? Maybe with JQuery and Ajax as support?
I suggest for you to use Spring Data Jpa, it's already have implemented pagination.
Your repository will look like this:
public interface MedicamentRepository extends JpaRepository<Medicament, Integer> {}
You can extend, as example, PagingAndSortingRepository interface, if you don't need some of methods that JpaRepository provides.
public class SomeClass{
#Autowired
public MedicamentRepository medicamentRepo;
public void someMethod(){
//in spring data jpa, page count starts from 0;
PageRequest pageRequest = new PageRequest(pageNumber,
pageSize); //also have sorting
org.springframework.data.domain.Page<Medicament> page = medicamentRepo.findAll(pageRequest);
}
}
you can read more here
Let's say there are #Service and #Repository interfaces like the following:
#Repository
public interface OrderDao extends JpaRepository<Order, Integer> {
}
public interface OrderService {
void saveOrder(Order order);
}
#Service
public class OrderServiceImpl implements OrderService {
#Autowired
private OrderDao orderDao;
#Override
#Transactional
public void saveOrder(Order order) {
orderDao.save(order);
}
}
This is part of working application, everything is configured to access single database and everything works fine.
Now, I would like to have possibility to create stand-alone working instance of OrderService with auto-wired OrderDao using pure Java with jdbcUrl specified in Java code, something like this:
final int tenantId = 3578;
final String jdbcUrl = "jdbc:mysql://localhost:3306/database_" + tenantId;
OrderService orderService = someMethodWithSpringMagic(appContext, jdbcUrl);
As you can see I would like to introduce multi-tenant architecture with tenant per database strategy to existing Spring-based application.
Please note that I was able to achieve that quite easily before with self-implemented jdbcTemplate-like logic also with JDBC transactions correctly working so this is very valid task.
Please also note that I need quite simple transaction logic to start transaction, do several requests in service method in scope of that transaction and then commit it/rollback on exception.
Most solutions on the web regarding multi-tenancy with Spring propose specifying concrete persistence units in xml config AND/OR using annotation-based configuration which is highly inflexible because in order to add new database url whole application should be stopped, xml config/annotation code should be changed and application started.
So, basically I'm looking for a piece of code which is able to create #Service just like Spring creates it internally after properties are read from XML configs / annotations. I'm also looking into using ProxyBeanFactory for that, because Spring uses AOP to create service instances (so I guess simple good-old re-usable OOP is not the way to go here).
Is Spring flexible enough to allow this relatively simple case of code reuse?
Any hints will be greatly appreciated and if I find complete answer to this question I'll post it here for future generations :)
HIbernate has out of the box support for multi tenancy, check that out before trying your own. Hibernate requires a MultiTenantConnectionProvider and CurrentTenantIdentifierResolver for which there are default implementations out of the box but you can always write your own implementation. If it is only a schema change it is actually pretty simple to implement (execute a query before returning the connection). Else hold a map of datasources and get an instance from that, or create a new instance.
About 8 years ago we already wrote a generic solution which was documented here and the code is here. It isn't specific for hibernate and could be used with basically anything you need to switch around. We used it for DataSources and also some web related things (theming amongst others).
Creating a transactional proxy for an annotated service is not a difficult task but I'm not sure that you really need it. To choose a database for a tenantId I guess that you only need to concentrate in DataSource interface.
For example, with a simple driver managed datasource:
public class MultitenancyDriverManagerDataSource extends DriverManagerDataSource {
#Override
protected Connection getConnectionFromDriverManager(String url,
Properties props) throws SQLException {
Integer tenant = MultitenancyContext.getTenantId();
if (tenant != null)
url += "_" + tenant;
return super.getConnectionFromDriverManager(url, props);
}
}
public class MultitenancyContext {
private static ThreadLocal<Integer> tenant = new ThreadLocal<Integer>();
public static Integer getTenantId() {
return tenant.get();
}
public static void setTenatId(Integer value) {
tenant.set(value);
}
}
Of course, If you want to use a connection pool, you need to elaborate it a bit, for example using a connection pool per tenant.
I am having a hard time deciding if I should stick with Hibernate for a new project, or get my feet wet with JPA and the new Spring Data implementation.
Is the Spring Data framework intended for large projects or small projects with modest query requirements?
While I certainly see the advantage in code reduction by using the #Query annotation, what do you do for dynamic queries? What about when you want to implement a save() method that's quite complex?
The documentation says to make a Custom interface and implementation that your main repository implements, but what if you need to access any super methods on the crud repository itself? The crud repository implements the custom one - not the other way around. It seems like an odd design.
I am very uncertain whether this framework will meet the challenges of complex and large applications. I've never ran into many problems with Hibernate, and I'm considering sticking with the good old reliable rather than go with Spring Data JPA.
What should I do? What unforeseen complications and costs will I encounter if I go with Spring Data JPA?
So, spring-data does some extra magic that helps with complex queries. It is strange at first and you totally skip it in the docs but it is really powerful and useful.
It involves creating a custom Repository and a custom `RepositoryImpl' and telling Spring where to find it. Here is an example:
Configuration class - point to your still-needed xml config with annotation pointing to your repositories package (it looks for *Impl classes automatically now):
#Configuration
#EnableJpaRepositories(basePackages = {"com.examples.repositories"})
#EnableTransactionManagement
public class MyConfiguration {
}
jpa-repositories.xml - tell Spring where to find your repositories. Also tell Spring to look for custom repositories with the CustomImpl file name:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<jpa:repositories base-package="com.example.repositories" repository-impl-postfix="CustomImpl" />
</beans>
MyObjectRepository - this is where you can put annotated and unannotated query methods. Note how this repository interface extends the Custom one:
#Transactional
public interface MyObjectRepository extends JpaRepository<MyObject, Integer>, MyObjectRepositoryCustom {
List<MyObject> findByName(String name);
#Query("select * from my_object where name = ?0 or middle_name = ?0")
List<MyObject> findByFirstNameOrMiddleName(String name);
}
MyObjectRepositoryCustom - repository methods that are more complex and cannot be handled with a simple query or an annotation:
public interface MyObjectRepositoryCustom {
List<MyObject> findByNameWithWeirdOrdering(String name);
}
MyObjectRepositoryCustomImpl - where you actually implement those methods with an autowired EntityManager:
public class MyObjectRepositoryCustomImpl implements MyObjectRepositoryCustom {
#Autowired
private EntityManager entityManager;
public final List<MyObject> findByNameWithWeirdOrdering(String name) {
Query query = query(where("name").is(name));
query.sort().on("whatever", Order.ASC);
return entityManager.find(query, MyObject.class);
}
}
Amazingly, this all comes together and methods from both interfaces (and the CRUD interface, you implement) all show up when you do:
myObjectRepository.
You will see:
myObjectRepository.save()
myObjectRepository.findAll()
myObjectRepository.findByName()
myObjectRepository.findByFirstNameOrMiddleName()
myObjectRepository.findByNameWithWeirdOrdering()
It really does work. And you get one interface for querying. spring-data really is ready for a large application. And the more queries you can push into simple or annotation only the better off you are.
All of this is documented at the Spring Data Jpa site.
I've used Spring Data JPA in small and large projects with simple query demands. The main advantage is from not even having to use the #Query annotation. There is nothing in Spring Data that prevents you from using it in large projects and the recent QueryDSLsupport might help you. This is an example of using QueryDSL to target Hibernate.
If you foresee complex queries and you feel comfortable using Hibernate objects without JPA I think an alternative combination could be to have the simple Spring Data Repositorys next to complex Hibernate-based ones with the specific methods you might need. It might be less cumbersome that twisting a Hibernate implementation into Spring Data JPA structure.
Spring JPA will provide you a lot of abstraction from writing SQL and even some HQL using query method declaration. Spring JPA shines with its query generation but when you want a purely hibernate solution you can customize as needed as spring JPA is still based on hibernate. Check the docs http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html for more info.