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.
Related
For personal education I am currently developing a little application framework around Guice to learn-by-doing how Spring etc. work behind the scenes.
Intro
Just for the sake of context, here is what I have so far and plan to do so you get a feeling for what I try to archive:
Context (Core)
ApplicationContext/-Configuration
Modules (auto discovered, setting up the Guice bindings)
Extensions
Config: #Config
Locale: #Locale and i18n services
Resources: #Property, #Resource and some classes providing easy access to resources
Persistence: Problems - there we go!
Question
I'd like to use the JDO standard (and its reference implementation DataNucleus) for the persistence layer. Setting up the PersistenceManagerFactory was easy, so was using it in a basic manner. I am however targeting a typical service / repository layer architecture, e.g.:
Person
PersonRepository (JDO)
PersonService (Transactions, using PersonRepository)
That alone wouldn't be too hard either, but as soon as I tried properly integrating transactions into the concept I got a bit lost.
Desired
class PersonService {
#Transactional(TxType.REQUIRED)
public Set<Person> doX() {
// multiple repository methods called here
}
}
class PersonRepository {
private PersistenceManagerFactory pmf;
public Set<Person> doX() {
try (PersistenceManager pm = pmf.getPersistenceManager()) {
pm.....
}
}
}
Difficulties
DataNucleus supports RESOURCE_LOCAL (pm.currentTransaction()) as well as JTA transactions and I would like to support both as well (the user should not have to distinguish between the two outside the configuration). He should not have to bother about transaction handling anyway, that is part of the annotation's method interceptor (I guess).
I'd love to support the #Transactional (from JTA) annotation that can be placed on service layer methods. Knowing that annotation is not per-se available in JDO, I thought it could be made usable as well.
How exactly should the repository layer "speak" JDO? Should each method get a PersistenceManager(Proxy)from the PersistenceManagerFactory and close it afterwards (as in the example) or get a PersistenceManager injected (rather than the factory)? Should each method close the PersistenceManager (in both scenarios)? That would not work with RESOURCE_LOCAL transactions I guess since a transaction is bound to one PersistenceManager.
What I tried
I have a JDOTransactionalInterceptor (working with pmf.getPersistenceManagerProxy) and a JTATransactionalInterceptor (very similar to https://github.com/HubSpot/guice-transactional/blob/master/src/main/java/com/hubspot/guice/transactional/impl/TransactionalInterceptor.java working with a ThreadLocal)
Summary
I am aware that my question may not be as clear as desired and mixes the service / repository layer questions (which is my main problem I think) and transaction stuff (which I could figure out once I understand how to properly use PMF/PM in repository layer I think)
There is no scope à la RequestScoped etc. I just want the first #Transactional method call to be the starting point for that whole thing (and that is the point: Is this impossible and the PMF/PM have to be scoped before and I have to direct my thinkings into that direction?)
Thanks for any clarification / help!
In a post last august sbzoom proposed a solution to make spring-data-mongoDB multi-tenant:
"You have to make your own RepositoryFactoryBean. Here is the example from the Spring Data MongoDB Reference Docs. You would still have to implement your own MongoTemplate and delay or remove the ensureIndexes() call. But you would have to rewrite a few classes to make sure your MongoTemplate is called instead of Spring's."
Did anybody implement this or something equivalent?
There's quite a few ways to skin the cat here. It essentially all boils down to on which level you'd like to apply the tenancy.
Basics
The basic approach is to bind some kind of key identifying the customer on a per-thread basis, so that you can find out about the customer the current thread of execution deals with. This is usually achieved by populating a ThreadLocal with some authentication related information as you can usually derive the tenant from the logged in user.
Now if that's in place there's a few options of where to apply the tenant knowledge. Let me briefly outline the most common ones:
Multi-tenancy on the database level
One way to separate data for multiple clients is to have individual databases per tenant. Spring Data MongoDB's core abstraction for this is the MongoDBFactory interface. The easiest way here is to override SimpleMongoDbFactory.getDb(String name) and call the parent method with the database name e.g. enriched by the tenant prefix or the like.
Multi-tenancy on the collection level
Another option is to have tenant specific collections, e.g. through tenant pre- or postfixes. This mechanism can actually be leveraged by using the Spring Expression language (SpEl) in the #Document annotation's collectionName attribute. First, expose the tenant prefix through a Spring bean:
#Component("tenantProvider")
public class TenantProvider {
public String getTenantId() {
// … implement ThreadLocal lookup here
}
}
Then use SpEL in your domain types #Document mapping:
#Document(collectionName = "#{tenantProvider.getTenantId()}_accounts"
public class Account { … }
SpEl allows you to refer to Spring beans by name and execute methods on them. MongoTemplate (and thus the repository abstraction transitively) will use the mapping metadata of the document class and the mapping subsystem will evaluate the collectionName attribute to find out about the collection to interact with.
I had a similar approach to Oliver Gierke.
At least on database-level. https://github.com/Loki-Afro/multi-tenant-spring-mongodb
You should be able to do things like this:
MultiTenantMongoDbFactory.setDatabaseNameForCurrentThread("test");
this.personRepository.save(createPerson("Phillip", "Wirth", ChronoUnit.YEARS.between(
LocalDate.of(1992, Month.FEBRUARY, 3),
LocalDate.now())));
System.out.println("data from test: " + this.personRepository.findAll());
// okay? fine. - lets switch the database
MultiTenantMongoDbFactory.setDatabaseNameForCurrentThread("test666");
// should be empty
System.out.println("data from test666: " + this.personRepository.findAll());
for springboot 2.3.3
overriding doGetMongoDatabase helped to achieve multi tenancy
protected MongoDatabase doGetMongoDatabase(String dbName) {
}
https://github.com/jose-m-thomas/mongo_multi_tenancy_spring_boot_2_3_3
Full featured multi-tenant/tenancy with Spring Boot + MongoDB + Spring MVC with shared/global database configuration.
https://github.com/arun2pratap/multitenant-spring-mongodb
Environment :
Spring 4 REST
Spring MVC
Hibernate
Issue :
We are developing an application with below stack.
The Spring REST web service will expose APIs for client which will display it on UI (ASP .NET ) . The response is sent in JSON.
Consider below scenario :
Client calls REST api to get User with ID. The dao layer fetches User entity and will be delievred to client.
And below issues/observations for above scenario :
Since User can have another entities related with it throgh Hibernate mapping (like userRoles using oneToMany), these entities also need to be fetched, else LazyInitialization exception is thrown since UI tries to access these collections through User object.
Not all properties in User object will be required in response (e.g: some requests won't need roles a user have).
Considering above picture in mind , what is the best design approach to send User object (or response) to client through Spring REST??
Create an intermediate layer of objects (like DTOs) mimicking entity objects. Have this DTOs populated in Service layer as per requirement. Since service layer runs inside transaction issue number 1 will be resolved. But this requires extra copying between entity and DTOs
Handle issue number 1/2 at Hibernate entity / query level (join fetch queries or revamping mapping) and exclude properties not required in response through annotations like: #JsonIgnore. But this approach is not flexible and requires very careful design of entity classes
Can anybody please comment on this?
Is there any better alternative available?
I strongly recommend to use DTOs level, here are several reasons:
At some point your REST representation will not be matched completely to DAO Entity. Here are few examples:
You need to return full list of lightweight user info (only user first and last name) for mobile version of your app
You want to provide User info loaded from DAO + some payment account info, retrieved from separate service.
You want to combine information from two separate DAO entities into one service call
etc.
Caching of data using some 3rd party library (EhCache, Hazelcast, etc.) or simple Map like structure - Custom serialization of Hibernate entities could become a big pain for entities with complex relationships.
With DTO level you have Service Interfaces/DTOs as an interface/client library for integration with other components. And you still safe to modify/completely redesign your DAO layer implementation, even switching to No SQL solution for example.
As a conclusion - using Hibernate Entities in REST API works fine for simple "Hello World" like apps and doesn't work for most of real life solutions.
Option 1 is the best approach
Create an intermediate layer of objects (like DTOs) mimicking entity
objects
Creating DTO object will make your design more flexible, All you have to do is to handle the DTO objects within the rest Controller not in the service Layer, that way you can use the same service Layer to produce many DTO's.
Copying between entity and DTOs, it is an extra work, but you can use a Mapper to handle that like Dozer
Consider this example :
#Service
public class MyService {
#Transactional
public User getUserBId(Long id){
User user = ....
return user;
}
}
Rest Controller:
#RestController
public UserRestController {
#Resource
private Myservice service;
#Resource
private Mapper mapper;
// here you can use a dto
#RequestMapping(...)
public UserDto getUser(#RequestParam()Long userId){
User user = service.getUserBId(userId);
return mapper.map(user,UserDto.class);
}
}
In such a scenariou you should ideally be using Hibernate4Module (reference Hibernate4Module github Link)
Using this will ensure the serialization of entities to JSON on the spring rest layer respects the lazy loaded attributes and will not try access them (or serialize them to JSON in your case).
I will list out a possible solution code below.
If you are using maven, these would be your dependencies :
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>2.3.0</version>
</dependency>
Create a class HibernateAwareObjectMapper and register the Hibernate4Module in it.
package com.mypackage.web;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module;
public class HibernateAwareObjectMapper extends ObjectMapper {
private static final long serialVersionUID = 1L;
public HibernateAwareObjectMapper() {
registerModule(new Hibernate4Module());
}
}
If you are using spring beans xml based config you could use something like below :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="...">
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.mypackage.web.HibernateAwareObjectMapper"/>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
</beans>
else in case you are using pure java based config as in Spring boot you can do it like this:
#Configuration
#EnableWebMvc
#EnableAsync
#ComponentScan(basePackages = { "com.mypackage.controller" }) // package referring to controllers
#PropertySource("classpath:imagesConfig.properties")
public class WebConfiguration
extends WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter
{
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters){
List<MediaType> supportedMediaTypes=new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
MappingJackson2HttpMessageConverter converter=new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(new HibernateAwareObjectMapper());
converter.setPrettyPrint(true);
converter.setSupportedMediaTypes(supportedMediaTypes);
converters.add(converter);
super.configureMessageConverters(converters);
}
}
Then your controllers could look something like this:
#RequestMapping(value="/doSomething", method=RequestMethod.POST, produces="application/json;charset=UTF-8")
public #ResponseBody MyCustomWebResponseObject<MyEntity> create(#Valid #RequestBody MyEntity myEntity) throws Exception {
// do whatever
}
Also Now if you want to still pass along a lazy loaded attribute of an entity,
then in your service/DAO layer or the layer which is annotated by #Transactional
you could do this :
Hibernate.initialize(myEntity.getLazyLoadedAttribute());
I hope this helps :)
do upvote and mark this as an answer if my answer helps you :)
What is the Java #Configuration equivalent to:
<repositories base-package="com.acme.repositories" />
in Spring Data JPA? I am trying to get rid of XML configuration in favour to #Configuration classes, however reading through JpaRepositoryConfigDefinitionParser sources is fruitless.
The closest what I can get is:
#Bean
public RepositoryFactorySupport repositoryFactory() {
return new JpaRepositoryFactory(entityManagerFactory().createEntityManager())
}
#Bean
public BookDao bookDao() {
return repositoryFactory().getRepository(BookDao.class)
}
However the <repositories/> tag is much more functional: it automatically creates DAO for all interfaces extending CrudRepository found on CLASSPATH. Also it seems like my solution does not apply transactions to DAOs as opposed to default Spring Data JPA behaviour.
Spring Data JPA introduced #EnableJpaRepositories. See the reference documentation for details.
Looks like not possible yet: https://jira.springsource.org/browse/DATAJPA-69
One of the most difficult things about understand Spring is that Spring supports multiple approaches to the same problem.
So in my application I using injected EntityManager using the #PersistanceContext annotation, for example:
#Repository
public class JpaDao extends JpaDaoSupport implements Dao {
#PersistenceContext(unitName = "PersistanceUnit", type = PersistenceContextType.EXTENDED)
private EntityManager em;
Is this approach compatible with extending JpaDaoSupport (which requires injecting in an EntityManager)? To me it looks like two incompatible approaches to the solving the same problem, but I would like some advice from someone who has more experience with Spring.
If I shouldn't be extending JpaDaoSupport, how should I build my DAO using the #PersistenceContext approach?
You're correct that they are two different approaches to the same problem. Which one is "better" is a matter of taste, I think. Using annotations has the benefit of avoiding Spring import dependencies in your code, and even the Spring JavaDoc for JpaDaoSupport suggests using them for new JPA projects. JpaDaoSupport is there to make Spring's support for JPA equivalent to its support for other ORM strategies (HibernateDaoSupport, JdbcDaoSupport, TopLinkDaoSupport, etc.). In those cases, annotation-based injection isn't an option.
For injecting the EntityManager you just need to add the next definition
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
you can get more information about this topic in this post from the oficial blog
I would rather recommend you not to extend JpaDaoSupport, spring will do everything for you. Follow the link suggested by diega for more information, same blog I have followed to upgrade my application to support spring - jpa.