How do I Stream<GroupJson> and entire database using JPA? - java

I am trying to stream in an entire database (about 22,000 records) via Spring JPA. Using the FindAll() method I can get them in, but they are all brought into memory at once. I want to stream them.
I have tried streamAll():
#Repository
public interface GroupJsonRepository extends CrudRepository<GroupJson, String> {
Stream<GroupJson> streamAll();
}
but I get a bizarre error:
No property streamAll found for type GroupJson!
My object is:
#Entity
#Table(name = "GroupJson")
public class GroupJson {
#Id
private String id;
private String hash;
private String jsonData;
private ZonedDateTime lastUpdatedTimeStamp;
...
Is there another repository I can use that does this? I can only find CrudRepository. OR, is there some other magic JPA key words that work? I am using Spring boot 1.5.9 and I am streaming data elsewhere, but I am using a custom call:
Stream<Authority> findByPartitionKey(Long partitionKey);

You can use query if too,
#Query("select gj from Customer gj")
Stream<GroupJson> streamAll();

You have to include the "By" part in the method declaration to enable Spring Data to parse your method name. Thats the reason why you get your strange error. Spring Data interprets streamAll as a property in your entity.
#Repository
public interface GroupJsonRepository extends CrudRepository<GroupJson, String> {
Stream<GroupJson> streamAllBy();
}

Related

Return List<String> or String in hibernate query without query annotation

I am using Spring Boot application. I am using following method to return List<String> using #Query
annotation.
#Query("select c.name from Customer c")
List<String> findAllNames();
Is there a way to get the above result without #Query or #NativeQuery annotations?
Spring Data JPA supports this quite well. You can define a POJO that represents the (reduced) object (the projection) you want of the entity, then define a method that describes the query, returns the appropriate type, and Spring Data will generate all the code necessary to execute the query and map the results.
Here's an example of a custom repository interface that uses this technique (untested pseudo-code):
public class CustomerSummary {
private String name;
// constructor and getters
}
#Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<CustomerSummary> getAllSummaries();
}
I'm not sure if you can use "simple" types such as String in the return type, but I wouldn't be surprised if that is supported.
Also, you might have to tweak the exact method naming of `` to make sure Spring Data correctly interprets it as you want. Here is some additional reference documentation.
You could use projection to select only name property:
interface CustomerNameProjection {
String getName();
}
and then use it in Repository class:
List<CustomerNameProjection> findAllProjectedBy();
edit: corrected repository method name
Inject EntityManager and call createQuery.
entityManager.createQuery("select c.name from Customer c")
.getResultList()

Marklogic - How to specify collection name externally in Java POJO (using spring JPA repository)

I am new to Marklogic and want to specify collection name externally in Java POJO. Currently spring JPA repository tries to search for collection with name as class name of pojo.
#Repository
public interface PersonRepository extends MarkLogicRepository<Person, String> {
}
public class Person {
#Id
private String personId;
private String personName;
}
So in above example, spring fetched data from "Person" collection which is fine for my local development. But for Dev and Test servers, I need to fetch data from versioned collection like "Person_V1"/ "Person_V2" which can be configured in application.properties file.
How can I make collection name configurable?
It is worth noting that MarkLogic collection is one of the document metadata categories. It is non-hierarchical and version|class-independent.
Seems to me you first attempt to map entity POJO to define collection which requires marshalling. The Annotation will be something like:
#Document(
uri = "/person_{version}/${GUID}.xml",
collectionPrefix = "{collection-prefix|null}",
collection = "${method.getName()}"
)
2ndly, you said you wish to cconfigure the collection in application.properties. Then the .properties file looks like this:
# prefix could be `Person_V1`, ` Person_V2` or null
person.collectionPrefix =
person.collection =
You can define the Annotation in the main class like:
#Value("${person.collectionPrefix}")
private String ***;
#Value("${person.collection}")
private String ***;
The optimised MarkLogic Java document operation boils down to:
metadata(Handle).getCollections().add(All)("{collection-array}");
What gives you the leeway, though, is to define POJO object as args in method to setCollections() | getCollections().
A full review of MarkLogic metadata Class and Method is:
https://docs.marklogic.com/javadoc/client/com/marklogic/client/io/DocumentMetadataHandle.html

Spring Boot Repository - load DTO's direct from the Database

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.

Get single column using Spring data JPA without query

I am just trying to avoid writing query and fetch data based on method name convention. Below is my Entity
#Entity
class Product{
#Id
private Integer productId;
private String productName;
private String productStrategy;
}
I have below repository:
interface ProductRepository extends JPARepository<Product,Integer>{
public Product findByProductStrategy(String productStrategy);
}
Above method and repository is working fine for me. But I am using only productName from the above result. So is there any way using which i can just fetch the productName instead of fetching hole record.
Note: I know, we can achieve it using #Query by writing HQL query or native query. But i want to do it without writing query, just with the method name convention.
Consider using Projections from spring data
Create a projection interface for your entity with the field that you want. In your case, it should look something like
public interface ProductName {
String getProductName();
}
and then change your repository to have the return type of the projection interface itself. Spring will take care of the rest.
public interface ProductRepository extends JPARepository<Product,Integer>{
public ProductName findByProductStrategy(String productStrategy);
}

No property findAllEntries found for type Entry

I don't know why Spring doesn't like my code:
I have Entry.java:
#Entity
#Table(name = "entries")
public class Entry {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "text")
private String text;
}
EntryDao.java:
public interface EntryDao extends JpaRepository<Entry, Long> {
List<Entry> findAllEntries();
}
EntryService.java:
#Service
public interface EntryService {
List<Entry> findAllEntries();
}
EntryServiceImpl.java:
public class EntryServiceImpl implements EntryService {
private EntryDao entryDao;
private SessionFactory sessionFactory;
#Override
#SuppressWarnings("unchecked")
public List<Entry> findAllEntries() {
Session session = this.sessionFactory.getCurrentSession();
List<Entry> entries = session.createQuery("from entries").list();
return entries;
}
}
This code gives me an error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entryDao': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property findAllEntries found for type Entry!
I don't understand how to handle this error and why this error occurs.
The root cause you got the exception is that you're against the convention/rules to declare/create queries in Spring Data JPA.
The official docs of Spring Data JPA mentioned that:
The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.
The central interface of abstraction is Repository, to manage your entity, you need to declare your own interface of Repository and JPA will help you to create proxy instances for those interfaces. There're already some base Repositories like CrudRepository or PagingAndSortingRepository to provide basic functionalities as you can tell from their names, so by extending those basic ones, you'll have many basic methods. To define more specific access methods, you need to follow the ways JPA provided to create queries:
Define method in your interface following the method name convention
Use #Query annotation to define it manually
For the first method, the docs of Query Create has detailed clarification, here's some key idea:
The mechanism strips the prefixes find…By, read…By, query…By, count…By, and get…By from the method and starts parsing the rest of it. The introducing clause can contain further expressions such as a Distinct to set a distinct flag on the query to be created. However, the first By acts as delimiter to indicate the start of the actual criteria. At a very basic level you can define conditions on entity properties and concatenate them with And and Or
Simply speaking, JPA will parse the method name and try to find the related property to create query criteria for you.
Now let's have a look at your code, if you just want to retrieve all of your entities, you don't need to define your own method, there's already findAll methods pre-defined, if you want to retrieve entities based on text content, it's supposed to look like:
Entity findByText(String text)
but your method findAllEntites just don't match any rules, so JPA throws such an error message to you.
As #AbdullahWasi said, just use the existing findAll() method from SpringData for your code. You might want to place a #Transactional annotation in your code, but that depends on your transaction boundaries.
Just remove your custom method from your Dao.
public interface EntryDao extends JpaRepository<Entry, Long> {
}
And use the default spring data findAll
#Transactional
public class EntryServiceImpl implements EntryService {
private EntryDao entryDao;
private SessionFactory sessionFactory;
#Override
#SuppressWarnings("unchecked")
public List<Entry> findAllEntries() {
return entryDao.findAll();
}
}

Categories