Spring Neo4J query abstract entity Attribute - java

I'm having trouble to grasp, use this correctly.
I have a AbstractEntity/AbstractGraphEntity class like this:
public abstract class GraphEntity extends AbstractEntity {
#GraphId
#Getter #Setter
#JsonIgnore
protected Long nodeId;
#Getter #Setter
#Index(unique = true, primary = true)
protected String id;
}
And a BaseGraphRepository like this:
#NoRepositoryBean
public interface BaseGraphRepository<T extends GraphEntity> extends GraphRepository<T> {
T findById(String id);
Collection<T> findByIdIn(Collection<String> ids);
T deleteById(String id);
Collection<T> deleteByIdIn(Collection<String> ids);
}
The idea is to have multiple entities that are extenting GraphEntity, and use several repositories extending the BaseGraphRepository.
But each time I query on: findById, it keeps giving me null.
So I've tried using, GraphEntity as a #NodeEntity, but that's a terrible idea, since then if I do query for an ID of one kind of entity, let's say a Category, but on an ProductRepository, it will give me the GraphEntity object.
So how should I approach this ?
Thanks!

I've managed to solve the findById/findByIdIn using the Neo4jRepository as such:
public interface BaseGraphRepository<T extends GraphEntity> extends Neo4jRepository<T, String> {}
But even with the Neo4jRepository the Generated Cypher for a custom query with the GraphEntity would be wrongly generated.
MATCH (n:GraphEntity) WHERE n.`id` ...
There might be some workaround to this, or it's a bad practice, I'm not sure.

Related

What is the best way to use findAll with Pageable in Spring?

I have employee class
#Entity
class Employee {
#Id
Integer id;
String name;
Integer age;
}
and another class EmployeeInfo:
class EmployeeInfo {
Integer id;
String name;
}
Now, I need to build a service to get a paginated list of EmployeeInfo by using findAll(Pageable pageable)
from the repository
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
what is the best way to do that? I want to avoid getting the page from findAll and create a new object of EmployeeInfo then adding it to a list in a loop
You can utilize spring-data-jpa projections.
There are many ways to use them (open/closed projections, class or interface based, etc.), but since you already have a EmployeeInfo class, it can be achieved by defining a new method in your repository:
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
Page<EmployeeInfo> getAllBy(Pageable pageable);
}
Note that your projection DTO class properties must exactly match properties in the aggregate root (entity class).
Also reference documentation suggest to define .equals() and .hashcode() methods.
Other methods can be found in the official documentation.

Fetch only some attributes from table using jpa findall java

I want to select just a few columns from a table.. The catch is that I'm using a specification and pagination from the front end filter, and I don't think I can concatenate those with criteriabuilder. My original idea was to create a #MappedSuperClass with the attributes I wanted (in this case, just the id and date), and fetch using a dao repository from an empty subclass. I have done something similar to this before and it worked, but the subclasses used different tables so it's a different ball game. In this case, since both subclasses use the same table, and there's nothing to differentiate between the classes other than one doesn't have any attributes, it keeps fetching the original bigger class. I want to avoid creating a view with just the columns I want or processing the data in the backend after the fetching, but I think that's the only possible solution.
Superclass
#MappedSupperClass
public class Superclass
{
#Column( name = "id" )
private Integer id;
#Column( name = "date" )
private Date date;
}
Original Subclass
#Entity
#Table( name = "table" )
public class OriginalSubclass
extends Superclass
{
#Column( name = "code" )
private Integer code;
#Column( name = "name" )
private String name;
}
New Subclass
#Entity
#Table( name = "table" )
public class NewSubclass
extends Superclass
{
}
I created a new dao for the new subclass
#Repository
public interface NewSubclassDao
extends JpaRepository<NewSubclass, Integer>, JpaSpecificationExecutor<NewSubclass>
{
}
Is there a way to get only the attributes I want with something similar to my idea?
Or is it possible to do it with criteriabuilder?
If none of the options are viable, would you prefer to use a view or process the data?
EDIT
To make it perfectly clear, I want Spring to bring me only the id and date attributes, using JPA findAll or something very similar, without messing the pagination or filter from the Specification.
You should be able to use #Query to do something like:
#Repository
#Transactional(readOnly = true)
public interface NewSubclassDao
extends JpaRepository<NewSubclass, Integer>, JpaSpecificationExecutor<NewSubclass>
{
#Query("SELECT table.code FROM #{#entityName} table")
public Set<Integer> findAllCodes();
}
There are many ways to do this, but I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(User.class)
public interface UserDto {
#IdMapping
Long getId();
String getName();
Set<RoleDto> getRoles();
#EntityView(Role.class)
interface RoleDto {
#IdMapping
Long getId();
String getName();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
UserDto a = entityViewManager.find(entityManager, UserDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<UserDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!

Spring JPA Projection findAll

Is it possible to use "findAll" for a JPARepository returning a Collection/List of Projections?
Example:
#Entity
public class Login {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
#GenericGenerator(name = "native", strategy = "native")
private Integer id;
private String name;
private String pass;
(...)
}
public interface LoginProjection {
public String getName();
}
#Repository
public interface LoginRepository extends JpaRepository<Login, Long> {
Login findByName(String name);
#Query(value = "SELECT name FROM login", nativeQuery = true)
List<LoginProjection> findAllLoginProjection();
}
Using #Query it works! But it is not possible to use
List<LoginProjection> findAll();
Because LoginProjection it does not extends T (Login).
I was thinking if it is possible to give a "alias" for findAll like findAllXYZ that does the same thing as findAll.
Using filters it works too, but I do not want to use them:
List<LoginProjection> findAllByName(String name);
My main goal would be something like this:
#Repository
public interface LoginRepository extends JpaRepository<Login, Long> {
Login findByName(String name);
List<Login> findAll();
List<LoginProjection> findAllLoginProjection();
}
Which is pretty easy and with "zero #Query"
And add a method to the repository:
List<LoginProjection> findAllProjectedBy();
The method name can be simplified to findBy() to match the documentation example at https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections
I assume you're not using Spring Data REST, so #Projection won't help here. #pavel-molchanov showed one form of expressing the projection, another form is:
List<LoginProjection> findBy();
If you have more than one projection, you can avoid creating a method in the repository for each projection by using dynamic projections like e.g. this:
<T> List<T> findBy(Class<T> projection);
// usage e.g.
... = findBy(LoginProjection.class);
... = findBy(UserSummaryProjection.class);
if you want to be more flexible you can add to the repository this method
<T> List<T> findBy(Class<T> projection);
and then you can call it with the following
List<LoginProjection> rows = loginRepository.findBy(LoginProjection.class);
The advantage of this approach is that you can use all the projections you want using the same query

(CRUD) Repository for a large number of JPA classes

I can create a repository via defining an interface on the appropriate JPA class A like the following:
public interface ARepository extends CrudRepository<A, Long>
{
}
and I can use that in my Controller (for example) via
#Autowired
private ARepository aRepository;
and just can do things like this:
aRepository.save(..);
aRepository.findAll();
..
No problem so far.
But my problem is that I have ca. 500 JPA classes and need to access each table which means to define 500 Repositories in the style of above.
So does exist an thing to create that either dynamically via some Spring Data "magic" which from my point of view should exist otherwise the above would not be possible. It looks like this is similar to my problem.
Apart from that one more issue related to the above. I can define findBy... methods in the interface and in the background there will be generated a query method for this particular attribute. The question is also if this can be done in a dynamic way related to the previous question, cause I have groups of tables which need supplemental query methods..
There is spring-data-generator which can automatically generate the interfaces for you.
Regarding your 2nd question I don't think you that can be done in a dynamic way. Java is statically compiled and there's no way to add members dynamically. There could be a tool that generates code for those methods but if that tool generates methods for all combinations of columns you will end up with a huge amount of methods.
You can make a base abstract entity for your 500 classes an then create one repo for this class. (I think it's a common practice to have a BaseEntity class with id, version etc. for every entity in the project).
For simple repo methods (like save, findAll etc.) it will work right from the box (note - entities must have the equal id type). For example:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstarct class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
}
#Entity
public class Entity1 extends BaseEntity {
private String name;
}
#Entity
public class Entity2 extends BaseEntity {
private String name;
}
public interface BaseEntityRepo extends JpaRepository<BaseEntity, Long> {
}
Note that BaseEntity must have #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) to prevent of using singe table base_entity for every entity. And their ids must not intersect (see #GeneratedValue(strategy = GenerationType.SEQUENCE)).
Usage:
#RunWith(SpringRunner.class)
#SpringBootTest
public class BaseEntityRepoTest {
#Autowired private BaseEntityRepo repo;
#Before
public void setUp() throws Exception {
repo.save(asList(
new Entity1("entity1"),
new Entity2("entity2")
));
}
#Test
public void readingTest() throws Exception {
List<BaseEntity> entities = repo.findAll();
assertThat(entities).hasSize(2);
}
}
Related to your second question you can use this approach:
public interface BaseEntityRepo extends JpaRepository<BaseEntity, Long> {
<T> T findById(Long id, Class<T> type);
}
Usage:
#Test
public void findById() {
final Entity1 entity1 = repo.findById(1L, Entity1.class);
final Entity2 entity2 = repo.findById(2L, Entity2.class);
assertThat(entity1).isNotNull();
assertThat(entity2).isNotNull();
}
But you can build repo query methods only for 'common' properties of inherited entities which are present in the base class. To make this method work you must move the name parameter to the BaseEntity:
<T> List<T> findAllByNameLike(String name, Class<T> type);

Spring-data : select distinct of a "sub class"

Here is my model:
#Entity
Class SubClass {
...
}
#Entity
Class MainClass {
...
private String name;
#ManyToOne
private SubClass subClass;
...
}
With the use of jpa data, I would like to have a select distinct of the SubClass.
I tried to do that with a repository:
public interface MainClassRepository extends JpaRepository<MainClass, Integer> {
public List<SubClass> findDistinctSubClassByName(String name);
}
The error I have as a result is the following:
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [MainClass] to type [SubClass]
I tried to create a Converter and add it as an entity, but Spring doesn't know it.
How can I do what I want? Either by registering my converter in the good place, or by doing something else that I haven't tought of yet?
Thanks!

Categories

Resources