In Spring data JPA ,How to query data from a table without a repository for entity - spring

Is it possible to fetch data from a table without creating a JPA repository for this specific table.
I need to do this as there are considerable number of entities which I have to do a simple query , it would be a waste to create repositories for each of them.

You can simply inject an EntityManager to any component:
#Component
class SomeComponent {
#PersistenceContext
private EntityManager entityManager;
public List<SomeEntity> findAllEntities() {
TypedQuery<SomeEntity> query = entityManager.createQuery("SELECT e FROM SomeEntity e", SomeEntity.class);
return query.getResultList();
}
}
Also, if your entities have the same superclass, you can use the same Repository for all of them, like described there.

Related

Unable to initialize lazy-loaded relationship inside of `#Transactional` method

I have a set of simple models like this (getters and setters omitted for brevity):
#Entity
public class Customer {
#Id
private Integer id;
}
#Entity
public class Order {
#Id
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "customer_id")
private Customer customer;
}
I am trying to load an Order using a Spring JPA repository with a findById method, including the customer.
First I tried this:
#Transactional
Optional<Order> findById(Integer id) {
return repository.findById(id);
}
But when I tried to access Customer I got a LazyInitializationException: could not initialize proxy - no Session. So after referring to some other questions, I updated my method to be a bit uglier, but to explicitly call Hibernate.initialize:
#Transactional
Optional<Order> findById(Integer id) {
return repository.findById(id)
.map( order -> {
Hibernate.initialize(order.getCustomer());
return order;
);
}
But I still get org.hibernate.LazyInitializationException: could not initialize proxy - no Session. repository is a regular CrudRepository which provides the findById method out-of-the-box.
How can I initialize this lazily loaded child entity? My understanding is that the #Transactional indicates that I should still be within the transaction for the entirety of this method call. The only thing further downstream is the repository itself, which is just an interface, so I'm not sure how else to go about forcing the load of this child entity.
The Order entity and everything else in it is retrieved properly from the database; it's only when I try to get the lazy-loaded child entities that we start having issues.
The only way I managed to get this working was to write a custom hql method in the Repository using a left join fetch. While that works, it clutters up my repository with a method that is pretty much a duplicate of another and which I'm pretty sure I'm not actually supposed to need (so I would rather not do it this way.)
Spring-Boot 2.1.4.RELEASE, Spring 5.1.6.RELEASE, Hibernate 5.3.7.Final.
You have to define the method as public. See "Method visibility and #Transactional" in the spring docs.
This should work:
#Transactional
public Optional<Order> findById(Integer id) {
Optional<Order> order = repository.findById(id);
order.ifPresent(o -> Hibernate.initialize(o.getCustomer()));
return order;
}

Can I use one repository interface to make calls to multiple entity tables using Spring JPA?

I have a scenario where I need to fetch data from 20 tables in an Oracle database. I have 20 entity classes and I am using Spring JPA for getting data. I am using Simple Crud operation for getting data like
{
public interface Object1Repository extends CrudRepository<Object1, Long> {
List<Object1> findAll();
}
}
same for table2
{
public interface Object2Repository extends CrudRepository<Object2, Long> {
List<Object2> findAll();
}
}
and so on for table3, table4 etc...
So my question is do I need to create 20 such interface repositories to fetch data from database or is there a better way of doing it something like:
{
public interface CommonRepository extends CrudRepository<GenericObject, Long> {
List<Object1> findAll();
List<Object2> findAll();
List<Object3> findAll();
List<Object4> findAll();
...
}
}
You will have to create a repository interface for each entity, and move the logic of querying the 20 tables to your #Service component which will act as a "Business Service Facade". You can annotate your method with #Transactionl to if you want to query all together, or non if one query fails.
As it was mentioned, you'll have to create each repository for each entity.
But you can call all the tables in one request if you connect the entities between them using JPA relations: #OneToMany, #ManyToOne, #ManyToMany or #OneToOne

Is this design of a Spring JPA DAO bad or improper?

I have been working to generalize the methods of the DAO for a project using Spring, JPA and Hibernate. However, I am still very much learning Spring, Java, and coding in general.
Is the below design bad or perfectly fine? Is there a better way to accomplish the same thing? Any advice would be greatly appreciated.
I have simplified the class:
#Repository
public class TestRepository
{
#PersistenceContext
private EntityManager entityManager;
public List<?> getListResults(Class<?> dtoClass, String sqlString)
{
List<?> returnList = null;
Query query = entityManager.createNativeQuery(sqlString, dtoClass);
try
{
returnList = (List<?>) query.getResultList();
}
catch (Exception e)
{
}
return returnList;
}
}
Spring Data JPA is the must convenient way in order to interact with your databases because it helps you to avoid the common mistakes that occurs when you try to configure your ORM mapping, entityManager, transacctionManager and all the rest of necessary components in order to establish a communication between your entity domains and your database.
For example you have a pojo like this:
#Entity
public class Item {
#Id
private Long id;
......
}
You can create an interface in order to get or put information to the item repository like this:
public interface ItemRepository extends from JpaRepository<Item,Long>{}
When you need to save the Item just #Autowired the ItemRepository, this is the must important part because the previous interface that is created without methods now exposes ready-to-work methods that will interact with your database, this is the abstraction level that makes Spring Data JPA very useful:
#Autowired
ItemRepository itemRepo
public void createItem(){
Item item = new Item();
itemRepo.save(item);
//or you can get information
List<Item> itemList = itemRepo.findAll();
}
More information in Spring Data JPA Documentation
How about using Spring Data Repositories?
#Repository
public interface SomethingRepository extends JpaRepository<Something, Long> {
}
That way you get lots of methods without having to manually write your SQL query as a string, you retain type safety and you can leverage the power of JPA queries and dynamic proxies that do this whole SQL business for you.

Spring Boot Data JPA - Modifying update query - Refresh persistence context

I'm working with Spring Boot 1.3.0.M4 and a MySQL database.
I have a problem when using modifying queries, the EntityManager contains outdated entities after the query has executed.
Original JPA Repository:
public interface EmailRepository extends JpaRepository<Email, Long> {
#Transactional
#Modifying
#Query("update Email e set e.active = false where e.active = true and e.expire <= NOW()")
Integer deactivateByExpired();
}
Suppose we have Email [id=1, active=true, expire=2015/01/01] in DB.
After executing:
emailRepository.save(email);
emailRepository.deactivateByExpired();
System.out.println(emailRepository.findOne(1L).isActive()); // prints true!! it should print false
First approach to solve the problem: add clearAutomatically = true
public interface EmailRepository extends JpaRepository<Email, Long> {
#Transactional
#Modifying(clearAutomatically = true)
#Query("update Email e set e.active = false where e.active = true and e.expire <= NOW()")
Integer deactivateByExpired();
}
This approach clears the persistence context not to have outdated values, but it drops all non-flushed changes still pending in the EntityManager. As I use only save() methods and not saveAndFlush() some changes are lost for other entities :(
Second approach to solve the problem: custom implementation for repository
public interface EmailRepository extends JpaRepository<Email, Long>, EmailRepositoryCustom {
}
public interface EmailRepositoryCustom {
Integer deactivateByExpired();
}
public class EmailRepositoryImpl implements EmailRepositoryCustom {
#PersistenceContext
private EntityManager entityManager;
#Transactional
#Override
public Integer deactivateByExpired() {
String hsql = "update Email e set e.active = false where e.active = true and e.expire <= NOW()";
Query query = entityManager.createQuery(hsql);
entityManager.flush();
Integer result = query.executeUpdate();
entityManager.clear();
return result;
}
}
This approach works similar to #Modifying(clearAutomatically = true) but it first forces the EntityManager to flush all changes to DB before executing the update and then it clears the persistence context. This way there won't be outdated entities and all changes will be saved in DB.
I would like to know if there's a better way to execute update statements in JPA without having the issue of the outdated entities and without the manual flush to DB. Perhaps disabling the 2nd level cache? How can I do it in Spring Boot?
Update 2018
Spring Data JPA approved my PR, there's a flushAutomatically option in #Modifying() now.
#Modifying(flushAutomatically = true, clearAutomatically = true)
I know this is not a direct answer to your question, since you already have built a fix and started a pull request on Github. Thank you for that!
But I would like to explain the JPA way you can go. So you would like to change all entities which match a specific criteria and update a value on each. The normal approach is just to load all needed entities:
#Query("SELECT * FROM Email e where e.active = true and e.expire <= NOW()")
List<Email> findExpired();
Then iterate over them and update the values:
for (Email email : findExpired()) {
email.setActive(false);
}
Now hibernate knows all changes and will write them to the database if the transaction is done or you call EntityManager.flush() manually. I know this won't work well if you have a big amount of data entries, since you load all entities into memory. But this is the best way, to keep the hibernate entity cache, 2nd level caches and the database in sync.
Does this answer say "the `#Modifying´ annotation is useless"? No! If you ensure the modified entities are not in your local cache e.g. write-only application, this approach is just the way to go.
And just for the record: you don't need #Transactional on your repository methods.
Just for the record v2: the active column looks as it has a direct dependency to expire. So why not delete active completely and look just on expire in every query?
As klaus-groenbaek said, you can inject EntityManager and use its refresh method :
#Inject
EntityManager entityManager;
...
emailRepository.save(email);
emailRepository.deactivateByExpired();
Email email2 = emailRepository.findOne(1L);
entityManager.refresh(email2);
System.out.println(email2.isActive()); // prints false

JPA/Hiberante don't generate join sql for FetchType.EAGER while Spring #Transactional annotated

I have a very wired problem.
JPA/Hiberante don't generate join sql for FetchType.EAGER while Spring #Transactional annotated. But if I remove the #Transactional . Everything is fine.
Here is the code:
public class Item {
#ManyToOne
private Order order;
}
public class Order {
#OneToMany(mappedBy = "item", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Item> items;
}
#Test
#Transactional
public void testFetch() throws Exception {
Item randomItem = new Item();
Order randomOrder = new Order();
//OrderService and itemService is implemented by Spring Roo standard.
orderService.saveOrder(randomOrder);
randomItem.setOrder(randomOrder);
itemService.saveItem(randomItem);
Order OrderResult = orderService.findOrder(randomOrder.getId());
final List<Item> itemSearchResult = OrderResult.getItems();
Assert.assertNotNull(itemSearchResult);
}
The assertNotNull will fail if #Transactional on. But will success if #Transactional commented.
I debug more information. Just to find out when #Transactional on Hibernate will not generate join sql for
orderService.findOrder(randomOrder.getId());
Alos I try to switch to elicpseLink as JPA provider. Things become worse, when #Transactional commented, orderService.findOrder(randomOrder.getId()) will return a empty list(not null, size 0).
Any advice? Many Thanks!
I can't comment on the joins in Hibernate except that you should specify fetch join in your query to be portable to other JPA providers. EclipseLink in particular does not join eager relationships without JPA settings or native query hints or #JoinFetch annotations.
As for the collection being empty on EclipseLink. This is because you only setting one side of the relationship. JPA requires you to set both sides of bidirectional relationships so that they remain consistent with what is in the database. When #Transactional is commented out, you are getting back the same randomOrder instance that had an empty items collection when persisted.
Try calling randomOrder.addItems(randomItem); and randomItem.setOrder(randomOrder); before the orderService.saveOrder(randomOrder); call.

Resources