so I have following hierarchy of entities:
#MappedSuperClass
public abstract class BaseEntity {
private Long id;
private Date createAt;
private Date updateAt;
}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Post extends BaseEntity {
private String creatorId;
private String title;
private String content;
}
#Entity
public class Article extends Post {
private String category; // article has category
}
#Entity
public class Journal extends Post {
private Date expiration; // journal has expiration
}
now when I use Spring Jpa Specification to query for articles with certain category, it won't work:
// define specification
public class ArticleSpecifications {
public static Specification<Article> withCategory(String category) {
(root, query, criteriaBuilder) ->
criteriaBuilder.equal(root.get("category"), category)
}
}
// repository
public interface PostRepository<T extends Post> extends JpaRepository<T, Long>, JpaSpecificationExecutor<T> { ... }
// usage: in some service class
#Autowired
private PostRepository<Article> articleRepository;
...
public void someMethod {
...
// error here
articleRepository.findAll(ArticleSpecifications.withCategory("news"), pageable);
...
}
Error message:
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [category] on this ManagedType [com.gladdev.galahad.base.BaseEntity]
Just trying to understand here why it tries to look up "category" in BaseEntity.
Every Specification accessing attributes defined in Post works just fine.
Is it some spring jpa specification bug or I missed something?
You can use the CriteriaBuilder.treat() method.
In Criteria API, downcasting an entity to a subclass entity can be performed by using one of the CriteriaBuilder.treat() methods:
See https://docs.oracle.com/javaee/7/api/javax/persistence/criteria/CriteriaBuilder.html#treat-javax.persistence.criteria.Root-java.lang.Class-
public static Specification<Article> withCategory(String category) {
return (root, query, criteriaBuilder) -> {
Root<Article> article = criteriaBuilder.treat(root, Article.class);
return criteriaBuilder.equal(article.get("category"), category);
};
}
Related
Need your help here in a Spring Data CrudRepository on how to do a findby of nested fields.
My class is of following structure and I need to query using truckId which is of the nested objects
#Document(collection = "unt-truck")
public class TruckModelDTO {
private String type;
private TestDTO testDTO;
}
Class TestDTO.java
public class TestDTO{
private TruckDTO truckDTO;
Private String version;
}
Class TruckDTO.java
public class TruckDTO {
private String truckId;
private String legacySystem;
}
Class TruckRepository.java
#Repository
public interface TruckRepository extends MongoRepository<TruckModelDTO, String> {
// TruckModelDTO findByTruckId(String truckid);
}
So how should I use findby for truckId which is inside a nested class?
You can use with either Entity or DTO as response. but you have to mention the relation between two or three Entity based on you requirement.
I have created Entity. you have to create DTO with same based on entity then apply main DTO as return type in JPA Repository query.
1.Entity
#Entity
public class TruckModel {
private String type;
// mention the relation based on your requirement
private Test test;
}
Entity
#Entity
public class Test {
// here mention the relation based on you requirement
private Truck truck;
Private String version;
}
3.Entity
#Entity
public class Truck {
private String truckId;
private String legacySystem;
}
Repository
#Repository
public interface TruckModelRepository extends MongoRepository<TruckModel, String> {
TruckModelDTO findByTestTruckTruckId(String truckid);
}
I have the following classes: MyEntity, MyEntityRepository, MyEntityCustomRepository, MyEntityCustomRepositoryImpl.
MyEntity:
#Document
class MyEntity {
#Id
private ObjectId id;
private final String name;
#JsonIgnore
private Boolean isDeleted = false;
#JsonIgnore
private Instant deletedAt;
}
MyEntityRepository:
#Repository
interface MyEntityRepository extends MongoRepository<MyEntity, ObjectId>, MyEntityCustomRepository {}
MyEntityCustomRepository:
public interface MyEntityCustomRepository {
List <MyEntity> someCustomMethod(Set<ObjectId> ids);
}
MyEntityCustomRepositoryImpl
class MyEntityCustomRepositoryImpl implements MyEntityCustomRepository {
private final MongoTemplate template;
MyEntityCustomRepositoryImpl(MongoTemplate template) {
this.template = template;
}
#Override
public List <MyEntity> someCustomMethod(Set<ObjectId> ids) {
Query query = new Query()
...
return template.find(query, MyEntity.class);
}
}
Now I want to modify all find/get/count etc methods in the MyEntityRepository by adding param Criteria.where("isDeleted).is(false) to all queries.
It's easy to add this query param to my custom method, but what will be the best way to override methods from the CrudRepository extended by the MyEntityRepository?
I just want to have a comment for what I've learned from dozens of samples about Generic DAO design-pattern. I added an inheritance hierarchy between POJO classes, DAO interfaces, and DAO implementations please see codes below
Legend:
DAOs (From Parent to children)
DAO implementations (From Parent to Children)
POJO classes (From Parent to Children)
The Data Acess Objects (Interfaces)
The GenericDAO interface
public interface GenericDAO<T> {
... some crud operations common to all objets
}
The PersonDAO interface
public interface PersonDAO<T extends Person> extends GenericDAO<T> {
... some operations unique to a person
}
The StudentDAO interface
public interface StudentDAO extends PersonDAO<Student> {
... some operations unique to a student
}
The Implementations
The GenericDAO Implementation
#Repository("genericDAO")
public class GenericDAOImpl<T extends Person> implements GenericDAO<T> {
private Class<T> type;
#SuppressWarnings("unchecked")
public GenericDAOImpl() {
this.type = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), GenericDAO.class);
System.out.println(type);
}
#Resource(name = "sessionFactory")
protected SessionFactory sessionFactory;
#Transactional
#Override
public Integer save(T entity) {
return (Integer) sessionFactory.getCurrentSession().save(entity);
}
#SuppressWarnings("unchecked")
#Transactional
#Override
public T get(Integer id) {
return (T) sessionFactory.getCurrentSession().get(type, id);
}
}
The PersonDAO implementation
#Repository ("personDAO")
public class PersonDAOImpl<T extends Person> extends GenericDAOImpl<T> implements PersonDAO<T> {
.. implemented methods for person
}
The StudentDAO implementation
#Repository("studentDAO")
public class StudentDAOImpl extends PersonDAOImpl<Student> implements StudentDAO {
.. implemented methods for student
}
The POJO Classes (Hibernate Annotated)
The Person Class (Parent Abstract Class)
#MappedSuperclass
public abstract class Person {
#Id
#GeneratedValue
#Column (name = "id")
private int id;
#Column (name = "name")
private String name;
#Column (name = "age")
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The concrete class (Student)
#Entity
#Table(name = "STUDENT")
public class Student extends Person {
#Column(name = "school")
private String school;
public Student() {
}
public Student(String school) {
this.school = school;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
}
I've been thinking about how am I going to construct a design-pattern between POJOs and DAO objects for days, Until I've come up with these design based on everything I've learned from different resources around the web. I've come up with the idea of DAO and DAO implementation inheritance based on the inheritance of the POJOs.
is this a good practice? reflecting the hierarchy of the POJOs and do it in DAOs?
am I doing something wrong about here with my design? because I have a complete program that
saves and retrieves my objects from the database without any problem
I'm open to any suggestion or corrections. Thank you in advance!!!
Not a comment on the design, but... have you consider using Spring Spring Data Jpa, which allows you to:
write your repository interfaces, including custom finder methods, and Spring will provide the implementation automatically.
I have the following Entity
#Entity
#Table(name="user_app")
public class User implements Serializable {
private static final long serialVersionUID = 4992188185008895307L;
private String id;
...
public User(){
}
#Id
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
The Id is of type String
I am working with Spring Data JPA, and I have the following:
public interface UserRepository extends JpaRepository<User, String> {
}
I am working with AOP and I have the following:
#Before("execution(* *..UserRepository.findOne(..))")
public void beforeRepositoryLogging(JoinPoint jointPoint){
logger.info(">>Before {} called", jointPoint.getSignature().getName());
}
It works fine.
But if I change from:
#Before("execution(* *..UserRepository.findOne(..))")
to
#Before("execution(* *..UserRepository.findOne(String))")
It does not works anymore. Why?
Until here according with the API:
JpaRepository
Interface JpaRepository<T,ID extends Serializable>
Where ID according with the API
Repository
ID - the type of the id of the entity the repository manages
Remember:
My repository has JpaRepository<User, String>, the ID therefore is String
and the findOne method signature is T findOne(ID id). How conlusion, the type of my ID is String.
Therefore what is missing?
Thanks in advance.
I'm experiencing some strange behavior when I'm looking up node entities with Spring Data Neo4j (SDN). If I use GraphRepository.findOne(long) it will return an entity with that identifier even though the entity is not of the same type.
This is what my (very) simplified entity structure looks like:
#NodeEntity
protected abstract class BaseEntity {
#GraphId
private Long id;
#JsonIgnore
#RelatedTo(type = RelationType.ENTITY_AUDIT)
private Audit audit;
}
#NodeEntity
public final class Person extends BaseEntity {
#Indexed(indexType = IndexType.FULLTEXT)
private String firstName;
#Indexed(indexType = IndexType.FULLTEXT)
private String lastName;
}
#NodeEntity
public class Audit extends BaseEntity {
#RelatedTo(type = RelationType.ENTITY_AUDIT, direction = Direction.INCOMING)
private BaseEntity parent;
private Long date;
private String user;
}
For every entity type, I've created repositories like this:
#Repository
public interface PersonRepository extends GraphRepository<Person> {}
#Repository
public interface AuditRepository extends GraphRepository<Audit> {}
I've got an abstract base class for my service layer classes. That is what they roughly look like:
public abstract class MyServiceImpl<T extends BaseEntity> implements MyService<T> {
private GraphRepository<T> repository;
public MyServiceImpl(final GraphRepository<T> repository) {
this.repository = repository;
}
#Override
public T read(final Long identifier) throws EntityNotFoundException {
return repository.findOne(identifier);
}
#Override
public T create(final T entity) {
return repository.save(entity);
}
}
#Service
public class PersonServiceImpl extends MyServiceImpl<Person> implements PersonService {
private PersonRepository personRepository;
#Autowired
public PersonServiceImpl(final PersonRepository personRepository) {
super(personRepository);
this.personRepository = personRepository;
}
}
When I execute the following code, the result is not as expected:
Person person = new Person();
person.setFirstName("Test");
person.setLastName("Person");
personService.create(person);
// suppose the person identifier is 1L
final Audit audit = auditRepository.findOne(1L);
You'd expect that the AuditRepository would return null, but this in not the case. Instead, it returns an Audit with identifier 1L and null in all of its properties. It seems that as long as there's a node that corresponds to a given identifier, it will be returned, no mather what its type is. If Person and Audit would have had matching property names, they would contain their values too... Is all this expected behavior, or am I missing something?
For now, I've solved this problem with the code below, where I do the type check myself.
public abstract class MyServiceImpl<T extends BaseEntity> implements MyService<T> {
private GraphRepository<T> repository;
public MyServiceImpl(final GraphRepository<T> repository) {
this.repository = repository;
}
#Override
public T read(final Long identifier) throws EntityNotFoundException {
return get(identifier);
}
protected T get(final Long identifier) throws EntityNotFoundException {
final T entity = repository.findOne(identifier);
final Class<T> type = getServiceType();
if (entity == null || !(type.equals(repository.getStoredJavaType(entity)))) {
throw new EntityNotFoundException(type, identifier);
}
return entity;
}
#SuppressWarnings("unchecked")
private Class<T> getServiceType() {
return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
If you need more configuration, please let me know.
My framework versions are:
<spring.version>3.2.0.RC1</spring.version>
<neo4j.version>1.8</neo4j.version>
<spring.data.neo4j.version>2.1.0.RELEASE</spring.data.neo4j.version>
we had that behavior before that it failed on the wrong entity type being returned, we changed that behavior so that the type you provide is used to automatically project the node to.
public <S extends PropertyContainer, T> T createEntityFromStoredType(S state, MappingPolicy mappingPolicy) {..}
template. createEntityFromStoredType(node, null) will get you the object with the stored state.
public Class getStoredJavaType(Object entity) {}
gives you the stored class for a node or relationship (or entity)
We had a discussion of changing the behavior back and failing esp. in Repositories.
The question is, what should happen then? An Exception? A Null result? ...
In general if you provide a raw node-id that is valid, returning an error or Null doesn't seem to be like a correct answer either?