I have an entity with a lot of relationships. I cannot change them because the mapping is used in numerous parts of the code.
In only one use case, I would like to be able to load only the entities and not their relationships.
I made a simple CRUDRepository like this :
public interface EmployeeRepository extends CrudRepository<Employee, UUID> {
List<Employee> findByCompanyId(UUID companyId);
}
How can I load Employee without their relationships without altering the mapping annotations?
I tried :
public interface EmployeeRepository extends CrudRepository<Employee, UUID> {
List<Employee> findLazyByCompanyId(UUID companyId);
}
This compiles but the entities are still not lazy loaded. I am surprised that the keyword 'Lazy' is accepted if lazy loading is not done.
Any idea?
There is no easy way. Possibly no way full stop -that would depend on your persistence provider. That is why should mostly define relationships as lazy and load eagerly when required rather than the other way around.
See, for example:
JPA and eclipselink - Overriding FetchType.Eager
and
How to override FetchType.EAGER to be lazy at runtime
All I could suggest would be to use a constructor expression to either return a list of unmanaged users.
http://www.objectdb.com/java/jpa/query/jpql/select#Result_Classes_Constructor_Expressions_
or, more simply use a Spring Data projection to return a subset of the data:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections
public interface EmployeeRepository extends CrudRepository<Employee, UUID> {
EmployeeSummaryProjection findByCompanyId(UUID companyId);
}
#Projection(name="EmployeeSummaryProjection", types = {Employee.class})
interface EmployeeSummaryProjection{
/declare methods matching the data you wish to return
}
If the data returned is read-only then either of the above may be a solution.
Related
I have just come upon something that I can't describe in any other way than bizarre.
I have a service that is supposed to do this:
it gets passed an external identifier of a customer
it looks up the customer's internal ID
then loads and returns the customer
I'm using optionals as there is a potential chance that external identifiers can't be resolved.
#Transactional(readOnly = true)
public Optional<Customer> getCustomerByExternalReference(String externalId, ReferenceContext referenceContext) {
return externalIdMappingService.resolve(externalId, referenceContext, InternalEntityType.CUSTOMER)
.map(x->new CustomerId(x.getTarget()))
.map(customerRepository::getById);
}
what's noteworthy is here is that: externalIdMappingRepository.resolve returns an Optional<ExternalIdReference> object. If that is present, I attempt to map it to a customer that I then look up from the database. customerRepository is a regular spring data JPA repository (source code below)
However, when trying to access properties from Customer outside the service, I get an exception like this:
org.hibernate.LazyInitializationException: could not initialize proxy [Customer#Customer$CustomerId#3e] - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:176)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:322)
at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:45)
at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95)
at Customer$HibernateProxy$R0X59vMR.getIdName(Unknown Source)
at CustomerApiModel.<init>(CustomerApiModel.java:27)
I understand that this means, that Hibernate decided to lazy load that entity. Once outside the transactional boundaries of the service, it's not able to load the data for that object anymore.
My Question is: Why does Hibernate/Spring Data try a lazy fetching strategy when I essentially just load a specific object by ID from a Spring Data Repository and how I can disable this behaviour the right way.
I'm aware that there is a couple of workarounds to fix the problem (such as allowing hibernate to open sessions at will, or to access properties of that object inside the service). I'm not after such fixes. I want to understand the issue and want to ensure that lazy fetching only happens when it's supposed to happen
Here's the code for customer (just the part that I think is helpful)
#Entity
#Table(name="customer")
#Getter
public class Customer {
#EmbeddedId
private CustomerId id;
#Embeddable
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public static class CustomerId implements Serializable {
private long id;
public long asLong() {
return id;
}
}
}
and here's the source code of the repository:
public interface CustomerRepository extends Repository<Customer, CustomerId> {
List<Customer> findAll();
Customer getById(CustomerId id);
Optional<Customer> findOneById(CustomerId id);
Optional<Customer> findOneByIdName(String idName);
}
By declaring the method Customer getById(CustomerId id); in your CustomerRepository interface, you chose to let your repostory selectively expose the corresponding method with the same signature from the standard spring-data repository methods, as explained by the Repository java doc:
Domain repositories extending this interface can selectively expose CRUD methods by simply declaring methods of the same signature as those declared in CrudRepository.
Different to what the doc says, this also includes methods from JpaRepository.
In the case of Customer getById(CustomerId id);, you therefore invoke the JpaRepository method with the same signature: T getOne(ID id);, which only invokes EntityManager#getReference , as suggested by it's doc:
[...] Returns a reference to the entity with the given identifier. Depending on how the JPA persistence provider is implemented this is very likely to always return an instance and throw an {#link javax.persistence.EntityNotFoundException} on first access. Some of them will reject invalid identifiers immediately. [...]
#see EntityManager#getReference(Class, Object) for details on when an exception is thrown.
When calling EntityManager#getReference, Hibernate first returns a non-initialized proxy of the Entity without executing any SQL statement at all, which is why your method only returns the non-initialized entity.
To fix this, you could change your service logic as follows:
#Transactional(readOnly = true)
public Optional<Customer> getCustomerByExternalReference(String externalId, ReferenceContext referenceContext) {
return externalIdMappingService.resolve(externalId, referenceContext, InternalEntityType.CUSTOMER)
.map(x->new CustomerId(x.getTarget()))
.map(id -> customerRepository.findOneById(id).get()); // <-- changed call
}
This way, spring-data would invoke CrudRepository#findById, which would internally call EntityManager#find and therefore return an initialized entity (or an empty Optional if none was found in the DB).
Related:
When use getOne and findOne methods Spring Data JPA
Why "findById()" returns proxy after calling getOne() on same entity? (attention when using getOne and findById in the same transaction)
In my application I use DTOs. My current solution in pseudocode is this - works well:
ResponseEntity<EntityDTO> RestController.get(String uuid){
EntityDTO dto = Service.get(uuid) {
Entity entity = Repository.loadEntity(id);
return EntityDTO.from(entity);
}
return ResponseEntity<EntityDTO>( dto , HttpStatus.OK);
}
Recently I saw an other solution without the transformation step in the service layer.
E.g. your Entity looks like this
:
#Entity
public class Book {
Long id;
String title;
String text;
.....
}
And the text is too 'heavy' to send it with the hole book you usually would create a DTO like this:
public class SlimBookDTO {,
static SlimBookDTO from(Book book) {
return new SlimBookDTO(book.id, book.title);
}
Long id;
String title;
.....
}
The "new" (for me) Solution is to create only an interface like this:
public interface SlimBookDTO {
Long getId();
String getTitle();
}
And your BookRepository gets a new method:
#Repository
public interface BookRepository extends JpaRepository<Book , Long> {
List<SlimBookDTO> findAllByTitle(String title);
}
With this method I don't need the service layer any more for direct requests. Is this common? Does somebody has experience with this? Has it some downsides that I can't see in a small application but will face in larger scale?
Those are couple of ways of returning data from the database.
You create DTO and map necessary fields and return
Other is create an interface which is directly a kind of return type from Repository. this is what we call as JPA interface projection.
For second one, you know in detail by referring below link
https://www.baeldung.com/spring-data-jpa-projections
JPA interface projections are very useful when we query two or more entities in the Repository class
This is totally fine for simple GETs if the objects are straightforward enough, although of course you can't add additional logic, formatting or constraints. But as long as you don't need to do that, this will work well.
I don't think Hibernate analyzes the dto to only select a few fields though, so if you want to improve the performance too you can define the queries yourself, i.e. #Query("select new com.bla.SlimbookDTO(book.id, book.title) from Book book"), at the cost of not being able to just use automagically generated queries anymore based on the method name.
I am not so into Spring Data JPA and I have the following problem working on a Spring Boot project.
I have the following architectural doubt about how to correctly handle this kind of situation:
I have a repository implemented by an interface like this in which I am defining my "query methods":
public interface ExcelRepository extends CrudRepository<Country, Integer> {
public List<Country> findAllByOrderByCountryNameAsc();
public List<BlockAttackDetail> findAllByOrderByAttackTypeAcronymAsc();
}
As you can see I am extending the CrudRepository interface and I specified a single model class named Country mapping a specific table on my database.
I added a second method working on another entity class (BlockAttackDetail) mapping a different database table.
So starting my application I obtain this error because this repository is intended only for the database table mapped by the Country entity class:
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property attackTypeAcronym found for type Country!
My problem is: Do I have to create multiple JPA repositories interfaces (one for each entity) or exist a way to have a single JPA repository interface working on multiple entity classes?
In my specific case I will have few methods that will interact with a specific datbase table (with an entity) and I prefear have a single repository interface handling multiple entity classes (to avoid confusion).
Can I do it in some way? And if I can do it make it sense from an architectural point of view or it is better have a specific JPA repository interface for each entity class?
According to Spring Data documentation you should have one repository for each entity
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.definition
In your case the only way to do your job is with Spring Data JPA:
public interface CountryRepository extends CrudRepository<Country, Integer> {
public List<Country> findAllByOrderByCountryNameAsc();
}
public interface BlockAttackDetailRepository extends CrudRepository<BlockAttackDetail, Integer> {
public List<BlockAttackDetail> findAllByOrderByAttackTypeAcronymAsc();
}
I have a generic JPA repository implementation dealing with many type of entities as follows:
#Component
#Transactional(transactionManager = "ubldbTransactionManager")
public class CatalogueRepositoryImpl {
...
#PersistenceContext(unitName = eu.nimble.utility.Configuration.UBL_PERSISTENCE_UNIT_NAME)
private EntityManager em;
public <T> void deleteEntity(T entity) {
if(!em.contains(entity)) {
entity = em.merge(entity);
}
em.remove(entity);
}
public <T> List<T> getEntities(String queryStr) {
Query query = em.createQuery(queryStr);
List<T> result = query.getResultList();
return result;
}
...
}
At some point I realized that some of the entities have not been deleted. Then, I found out that some managed entities cause cancellation of the removal, as described at: https://stackoverflow.com/a/16901857/502059
Since the method is generic, it included various types of entities inside. As a work around, I wanted to get rid of the entities causing cancellation of deletion and I added em.flush() and em.clear() at the beginning of the deleteEntity method. Although this worked, I feel that this is a dirty workaround.
So, I'm asking some best practices for such a case. For example, would creating a new EntityManager in deleteEntity be an alternative? I didn't want this since I wanted Spring to handle managing the scope of EntityManagers and transactions.
One last point about Spring-managed EntityManager: I also wonder whether the em in the example managed by Spring is an application-scope one? If so, wouldn't it keep all the retrieved entities and expand continuously?
If you're using Hibernate, you don't need to do the merge prior to deleting the entity. That's only a JPA requirement, but Hibernate is more lenient in that regard.
You could also do something like:
entity = em.getReference(entity.getClass(), entity.getId());
em.remove(entity);
If that does not work, it could be because you are not cascading the REMOVE operation to child associations.
You can make the T argument extend an Identifiable interface which defines the getId method and have your entities implement this interface so that your method is more generic.
I am using generics to build a DAO. The database could be any database. The problem is that for each type of class there is a specific class. For example:
public class DAO<T> {
public void save(T entity) {
}
}
public class StudentDAO extends DAO<Student> {
}
Just imagine I have 1000 tables or more. Do I need to have 1000 classes like this? Is there a better way to design this?
Edit
I am using MongoDB a NoSQL database with Spring MongoDB. It has Repository concept through Spring but I will still end up with 1000 classes. I cannot use JPA or Hibernate. Any other solution?
You can do it. But I'd recommend you to use Generic Dao project. It supports both native Hibernate and JPA API and allows you to create one and only one DAO for all entities you have.
You don't have to extend DAO class. I assume DAO constructor has a parameter to detect which entity and which table it should interact with. Something like this:
public DAO(Class<T> type) {
this.persistentType = type;
}
With such constructor, wherever you need a DAO instance for Student entity you can initialize it like this:
DAO<Student> studentDao = new DAO<Student>(Student.class);
Yes there is definitely a better way. What you bumped into is what I call the "DAO-per-entity scalability issue". What you need for that is a reusable Generic DAO implementation e.g. PerfectJPattern
IGenericDao<Long, Customer> myCustomerDao = HibernateDaoFactory.getInstance().createDao(Customer.class);
// create a Customer
Customer myCustomer1 = new Customer(/*name=*/"Pedro");
myCustomerDao.create(myCustomer1);
// find all customers whose name is "Juan"
List<Customer> myMatches = myCustomerDao.findByExample(new Customer(/*name=*/"Juan"));
what just happened here? you don't need to create a new CustomerDao but reuse the generic one. In addition to the basic CRUD, you may even cover 90% of your "finder" needs using the findByExample see IGenericReadOnlyDao.
If the findByExample does not cover all your needs then you have the choice to use the Spring level Generic DAO, example here that offers direct mapping from SQL to your DAO interface and you don't need to provide an implementation.
Consider Spring Data , which will generate DAO layer for you.
You consider use hibernate + spring