I have an abstract class and its child.
#Getter
#Setter
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#EntityListeners(AuditingEntityListener.class)
#DiscriminatorValue(EntityEnum.Values.APPEALS)
public class Appeals extends EntityAbstract {
public Appeals(String content) {
this.content = content;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Type(type = "text")
#Column(name = "guid", updatable = true, insertable = true)
private String guid;
#Type(type = "jsonb")
#Column(name = "content", columnDefinition = "jsonb")
private String content;
#LastModifiedDate
#Column(name = "changed_at", columnDefinition = "TIMESTAMP")
private LocalDateTime changedAt;
#CreatedDate
#Column(name = "created_at", columnDefinition = "TIMESTAMP")
private LocalDateTime createdAt;
}
#ToString
#Getter
#Setter
#Entity
#Table(name = "entity_parent")
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#EntityListeners(AuditingEntityListener.class)
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
//#MappedSuperclass
#TypeDefs({#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)})
public abstract class EntityAbstract implements Content {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "guid")
private String guid;
#Version
private Integer version;
}
I use Spring Data and try to make a request:
public interface EntityRepository extends JpaRepository<EntityAbstract, Long>, JpaSpecificationExecutor<EntityAbstract> {
#Query("SELECT e FROM EntityAbstract e WHERE TYPE(e) = :type AND e.guid = :guid")
<E extends EntityAbstract> E findByGuidAndType(String guid, Class<E> type);
}
I make the call like this:
entityRepository.findByGuidAndType(guid, Appeals.class);
I get this error:
org.hibernate.QueryException: Named parameter [type] not set; nested exception is java.lang.IllegalArgumentException: org.hibernate.QueryException: Named parameter [type] not set
What am I doing wrong?
I believe that there is a much more easy way to solve the problem. You are using a type class parameter Class<E> in the findByGuidAndType(String guid, Class<E> type) method whereas you shoudl use a <? extends EntityAbstract> type because in a first case Spring knows nothing about a E but if you will declare it as an inheritor of the EntityAbstract Spring will start match classes properly. So the repo interface will be like this:
public interface EntityRepository extends JpaRepository<EntityAbstract, Long>, JpaSpecificationExecutor<EntityAbstract> {
#Query("SELECT e FROM EntityAbstract e WHERE TYPE(e) = :type AND e.guid = :guid")
<E extends EntityAbstract> E findByGuidAndType(String guid, Class<? extends EntityAbstract> type);
}
At least it works for me as expected
This is how I solved the problem:
#Override
#Transactional
public ResponseDto findByGuid(String guid, Class<?> name) {
Optional<EntityAbstract> guid1 = entityRepository.findOne(((Specification<EntityAbstract>)
(root, query, builder) ->
findInField(guid, () -> root.get("guid"), builder, query))
.and((root, query, builder) ->
builder.equal(root.type(), builder.literal(name))
)
);
EntityAbstract entityAbstract = guid1.orElse(null);
return representateToDto(entityAbstract);
}
private Predicate findInField(Object element,
Supplier<Path<?>> pathSupplier,
CriteriaBuilder builder,
CriteriaQuery<?> query) {
query.distinct(true);
return StringUtils.isEmpty(element)
? builder.conjunction()
: pathSupplier.get().in(element);
}
Related
I need to get a custom json containing the fields of the class Utilisateur plus a fied (centre_rc) of another class Operateur
I tried ti implement this using #Transient with centre_rc in Utilisateur.java but my json is always null. Now i tried to get Object[] in UtilisateurRepository.java (the code is shown below) but I get this error class java.math.BigInteger cannot be cast to class com.mycompany.myapp.domain.Utilisateur (java.math.BigInteger is in module java.base of loader 'bootstrap'; com.mycompany.myapp.domain.Utilisateur is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader #51ff3ed4)
here is Utilisateur class
#Entity
#Table(name = "utilisateur")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Utilisateur implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
#Column(name = "id")
private Long id;
#Column(name = "nom_utilisateur")
private String nomUtilisateur;
#Column(name = "prenom")
private String prenom;
#Column(name = "nom")
private String nom;
#Column(name = "date_inscription")
private LocalDate dateInscription;
#Column(name = "password")
private String password;
/*
#Transient
#JsonSerialize
private String centre_rc;
*/
#ManyToOne
#JsonIgnoreProperties(value = { "utilisateurs" }, allowSetters = true)
private Operateur operateur;
//getters & setters
}
here is Operateur class
#Entity
#Table(name = "operateur")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Operateur implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
#Column(name = "id")
private Long id;
#Column(name = "centre_rc")
private String centreRc;
#Column(name = "numero_rc")
private String numeroRc;
#OneToMany(mappedBy = "operateur")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
#JsonIgnoreProperties(value = { "operateur" }, allowSetters = true)
private Set<Utilisateur> utilisateurs = new HashSet<>();
//getters & setters
}
In UtilisateurRepository.java here is my SQL query:
#Query(value = "select u.*, o.centre_rc from utilisateur u inner join operateur o on o.id=u.operateur_id", nativeQuery = true)
Page<Object[]> findAllOperateurs(Pageable pageable);
In UtilisateurResource.java
#GetMapping("/utilisateursOperateurs")
public ResponseEntity<List<Object[]>> getAllUtilisateursCalledOperateurs(
#org.springdoc.api.annotations.ParameterObject Pageable pageable
) {
log.debug("REST request to get a page of Utilisateurs Operateurs");
Page<Object[]> page = utilisateurRepository.findAllOperateurs(pageable);
for(Object[] obj : page){
Utilisateur utilisateur = (Utilisateur) obj[0];
String centre_rc = (String) obj[1];
}
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
return ResponseEntity.ok().headers(headers).body(page.getContent());
}
UPDATE
I tried #Transient field called centre_rc in Utilisateur class but the json return null for this field
findAllOperateurs doesn't return an array with first object is a Utilisateur and second object is a String like that.
When you run this query directly in DB, each row it returns is an element in the page array.
By accessing obj[0], you're accessing the first column of the row which is the id of type BigInteger, you can't cast a BigInteger to Utilisateur.
You can cast obj Utilisateur.
I have these entities where Shop entity is parent:
#Data
#NoArgsConstructor
#Entity
#DynamicUpdate
#Table(name = "Shop", schema = "public")
public class ShopDao {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String name;
private String processedStatus;
#OneToMany(mappedBy = "shopDao", cascade = CascadeType.ALL, orphanRemoval = true)
private List<BookDao> bookDaoList;
}
#Data
#NoArgsConstructor
#Entity
#ToString(exclude = {"shopDao"})
#Table(name = "Book", schema = "public")
public class BookDao {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String name;
private String author;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "other_id", referencedColumnName = "id")
private OtherDao otherDao;
#ManyToOne
#JoinColumn(name = "shop_id", nullable = false)
private ShopDao shopDao;
}
#Data
#NoArgsConstructor
#Entity
#ToString(exclude = {"bookDao"})
#Table(name = "Other", schema = "public")
public class OtherDao {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String metadata;
#OneToOne(mappedBy = "otherDao", fetch = FetchType.EAGER)
private BookDao bookDao;
}
And these are repos:
#Repository
public interface ShopRepo extends JpaRepository<ShopDao, Long> {
#EntityGraph(attributePaths = {"bookDaoList"})
List<ShopDao> findAllByProcessedStatus(String processedStatus);
}
#Repository
public interface BookRepo extends JpaRepository<BookDao, Long> {
}
#Repository
public interface OtherRepo extends JpaRepository<OtherDao, Long> {
}
When i'm using findAllByProcessedStatus() function, i get BookList inside Shop object correctly, but each Book can't reach their Other objects and i get LazyInitializationException:
screenshot
How do i fix that problem?
Actually, with spring data's #EntityGraph all you need is :
#Repository
public interface ShopRepo extends JpaRepository<ShopDao, Long> {
#EntityGraph(attributePaths = {"bookDaoList.otherDao"})
List<ShopDao> findAllByProcessedStatus(String processedStatus);
}
This is the most convenient way.
For more complex relations, you could define a #NamedEntityGraph, and provide subgraphs, like so.
What I find intriguing, is that the BookDao is the owner of this relation, so I would expect it to be eagerly loaded, since you haven't specified a the Lazy fetch mode explicitly ...
I have an API developed on Java using Spring Boot and Spring Data. I'm having a ClassCastException error when I try to get one entity using the method findById(id) from JPARepository
The exception received is:
java.lang.ClassCastException: class com.sun.proxy.$Proxy83 cannot be cast to class package.repository.FAQRepository (com.sun.proxy.$Proxy83 and package.repository.FAQRepository are in unnamed module of loader 'app')
I received it when I try to call to JPARepository.findById(id) However I'm getting the same error on other places calling other spring-data-jpa methods like reposory.save(Entity).
FAQ faq = this.repository.findById(updateFAQ.getId()).orElseThrow(() -> new NotFoundEntityException("FAQ not found"));
FAQsRepoitory:
#Repository
public interface FAQRepository extends GenericRepository<FAQ> {
List<FAQ> findByOperative(Operative operative);
}
GenericRepository:
#Repository
public interface GenericRepository<Entity extends GenericPersistentEntity> extends JpaRepository<Entity, Long> {
}
My entity:
#Entity
#Table(name = "faqs")
#Getter
#Setter
#SQLDelete(sql = "UPDATE faqs SET deleted_date=NOW() WHERE id=?")
public class FAQ extends GenericPersistentEntity {
#Lob
private String question;
#Lob
private String answer;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "operative_id")
private Operative operative;
}
Superclass entity:
#MappedSuperclass
#Where(clause = "deleted_date is null")
public abstract class GenericPersistentEntity implements GenericPersistentInterface {
#Getter
#Setter
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "creation_date", nullable = false)
private Date creationDate;
#UpdateTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "last_update_date")
private Date updateDate;
#Column(name = "deleted_date")
private Date deletedDate;
}
Any help will be appreciated. Thanks.
I have an entity called Recipe and I created a Repository which extends JpaRepository and I would like to search up the recipes which contains every element of the search array for dietLabelList.
#Getter
#Setter
#Entity
#NoArgsConstructor
#RequiredArgsConstructor
#ToString
#Table(name = "recipe")
public class Recipe {
#Column(name = "id", nullable = false)
#Id
#GeneratedValue
private UUID rid;
#Column(name = "title", nullable = false)
private String title;
#Column(name = "dietLabelList", nullable = false)
private UUID[] dietLabelList;
}
#Repository
public interface RecipeRepository extends JpaRepository<Recipe, UUID> {
List<Recipe> findByTitleContaining(String title);
List<Recipe> findByDietLabelList(UUID[] dietLabels);
}
e.g. I have a recipe that has as a dietLabelList like this one ["Balanced", "High-Fiber", "High-Protein"] and findByDietLabelList(["Balanced", "High-Fiber"]) should be able to find it. Is something like possible with JpaRepository?
You can use QueryParam and specify your custom Query
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
#Repository
public interface RecipeRepository extends JpaRepository<Recipe, UUID> {
List<Recipe> findByTitleContaining(String title);
#Query(value = "SELECT r FROM Recipe r WHERE r.dietLabels in :dietLabels")
List<Recipe> findByDietLabelList(#Param("dietLabels") UUID[] dietLabels);
}
Father.java
#Entity
#Table(name = ClinicManagementVariableUtils.TABLE_NAME_FOR_FATHER)
#JsonInclude(Include.NON_EMPTY)
#NamedQueries({
#NamedQuery(name = "father.findAll", query = "SELECT f FROM Father f")
})
#NamedEntityGraphs({
#NamedEntityGraph(
name = "graph.father.setOfChildrens",
attributeNodes = #NamedAttributeNode(value = "setOfChildrens")),
})
})
public class Father {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
#Column(name = "fatherId", nullable = false, insertable = false, updatable = false)
private String id;
#Column(name = "name", columnDefinition = "varchar(50)")
private String name;
#Column(name = "firstName", columnDefinition = "varchar(50)")
private String firstName;
#Column(name = "lastName", columnDefinition = "varchar(50)")
private String lastName;
#Column(name = "degree", columnDefinition = "varchar(50)")
private String degree;
#OneToOne(mappedBy = "father")
#JsonIgnore
private Mother mother;
#OneToMany(mappedBy = "father") // children
private Set<Children> setOfChildrens;
getter()
setter()
}
Children.java
#Entity
#Table(name = ClinicManagementVariableUtils.TABLE_NAME_FOR_CHILDREN)
#JsonInclude(Include.NON_EMPTY)
public class Children {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
#Column(name = "childrenId", nullable = false, insertable = false, updatable = false)
private String id;
#Column(name = "name", columnDefinition = "varchar(50)", nullable = false)
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JsonIgnore
private Father father;
getter()
setter()
}
Mother.java
#Entity
#Table(name = ClinicManagementVariableUtils.TABLE_NAME_FOR_MOTHER)
#JsonInclude(Include.NON_EMPTY)
public class Mother {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
#Column(name = "motherId", nullable = false, insertable = false, updatable = false)
private String id;
#Column(name = "name", columnDefinition = "varchar(50)", nullable = false)
private String name;
#OneToOne
#JoinColumn(name = "fatherId")
private Father father;
getter()
setter()
}
FatherDao.java
public interface FatherDao extends GenericModelDao<Father> {
// Note : Return type is Mother instead of Father
public List<Mother> getFathersUsingNativeQueryAndEntityGraph();
}
FatherDaoImpl.java
#Named
public class FatherDaoImpl extends GenericModelDaoImpl<Father> implements FatherDao {
#PersistenceContext
EntityManager entityManager;
#Override
public List<Mother> getFathersUsingNativeQueryAndEntityGraph() {
EntityGraph graph = entityManager.getEntityGraph("graph.father.setOfChilrensAndAddresses");
List<Mother> list = entityManager.createNamedQuery("father.findAll").setHint("javax.persistence.fetchgraph", graph)
.getResultList();
return list;
}
}
FatherService.java
public interface FatherService {
// Note : Return type is Mother instead of Father
public List<Mother> getFathersUsingNativeQueryAndEntityGraph();
}
FatherServiceImpl.java
#Named
public class FatherServiceImpl implements FatherService {
#Inject
private FatherDao fatherDao;
#Override
public List<Mother> getFathersUsingNativeQueryAndEntityGraph() {
return fatherDao.getFathersUsingNativeQueryAndEntityGraph();
}
}
FatherController.java
#Controller
public class FatherController {
private static final Logger LOGGER = LoggerFactory.getLogger(FatherController.class);
#CrossOrigin
#RequestMapping(value = "/getFathersUsingNativeQueryAndEntityGraph", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public List<Mother> getFathersUsingNativeQueryAndEntityGraph() {
List<Mother> list = new ArrayList<>();
try {
// Note : now list of father object is return as list of mother but it working..
list = fatherService.getFathersUsingNativeQueryAndEntityGraph();
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
}
In Father.java, there are 2 type of annotations:
Native Query
Entity Graph
Now in DAO layer, I execute native query with graph query in getFathersUsingNativeQueryAndEntityGraph() method which will return list of fathers.
But when I fetch it in List<Mother> then also it returns all fields from Father.java even if those fields not exists in Mother.java.
Doubt:
If it returns all Fields of Father.java then how it is possible that it returns response in Mother.java?
1. Since you have no type checks anywhere this code will always compile.
2. As long as you never access an object in that list specifically as a Mother object, your code will run.
When you use createNamedQuery without a class parameter, you create an untyped Query object, which returns a raw list with whatever objects JPA returns.
In your code this list will contain Father objects, because that is what you asked for.
Writing List<Mother> list = ... is wrong, but your compiler doesn't know this. Since the type parameter is only checked at compile time, this code will run and during runtime list will be a generic list holding Father objects.
You should get a compiler warning here about turning that generic list into a List<Mother>, because the compiler cannot guarantee that this is correct. Listen to such warnings, they are there for a reason.
Only when you do this:
Mother mother = list.get(0);
will you get a runtime error, specifically a ClassCastException. However, your compiler will not complain about this, because it believes your list will contain Mother objects, because you lied to it above.
Fix your code
You should use a TypedQuery instead, by passing the class you expect to createNamedQuery:
entityManager.createNamedQuery("father.findAll", Father.class)
This will enforce the correct type during compile time, so doing List<Mother> list = ... will not compile anymore.