I'm playing around with generics and Spring Data repository. I created a simple project with almost zero configuration, entities are in subpackage of main class.
Entity
#Data
#ToString(callSuper = true)
#Entity
public class Person extends GenericEntity {
private String name;
}
#Data
#MappedSuperclass
public class GenericEntity {
#Id
#GeneratedValue
private Integer id;
#CreationTimestamp
#Column(name = "TMS_INSERIMENTO")
private LocalDateTime tmsInserimento;
#UpdateTimestamp
#Column(name = "TMS_AGGIORNAMENTO")
private LocalDateTime tmsAggiornamento;
}
Repository
public interface GenericRepository<T extends GenericEntity> extends JpaRepository<T, Integer> {
}
Service
public List<Person> findAllPeople() {
return genericRepository.findAll();
}
Call to findAll() throws the following exception:
org.springframework.dao.InvalidDataAccessApiUsageException: Not an entity: class com.example.demot.entity.GenericEntity; nested exception is java.lang.IllegalArgumentException: Not an entity: class com.example.demot.entity.GenericEntity
Try with the following
#Data
#MappedSuperclass
public class GenericEntity <T extends GenericEntity> {
...
}
And then
#Data
#ToString(callSuper = true)
#Entity
public class Person extends GenericEntity<Person> {
...
}
And then you need the generic repository which should return generic entities not specific persons
public interface GenericRepository extends JpaRepository<GenericEntity, Integer> {
}
which can be called in service as
public List<GenericEntity> findAllGenericEntities() {
return genericRepository.findAll();
}
And then you can also have a person repository
public interface PersonRepository extends JpaRepository<Person, Integer> {
}
which can be called in service as
public List<Person> findAllPersons() {
return personRepository.findAll();
}
Related
I have a SpringBoot 2 app that uses using Couchbase as a database, Spring-Boot and Spring-Data and Lombok fot the getters and equals method
I have created this Repository
#ViewIndexed(designDoc = "bendicionesDoc")
public interface BenRepository extends CouchbaseRepository<BendicionesDoc, String> {
#Query("#{#n1ql.selectEntity} where #{#n1ql.filter} AND ANY uuid IN data.identifier.id SATISFIES uuid = $1 END")
List<BendicionesDoc<Item>> findById(String id);
}
and here all the objects created with Lombok library
public class BendicionesDoc<T>implements Serializable {
#Field
private T data;
}
and
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(NON_NULL)
public class Item {
private List<Identifier> identifier;
}
and
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(NON_NULL)
#EqualsAndHashCode
public class Identifier {
private String id;
private MasterServant idContext;
private MasterServant idScope;
}
and
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(NON_NULL)
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class MasterServant {
private String context;
#JsonValue
#EqualsAndHashCode.Include
private String value;
private Name valueDescription;
#JsonCreator
public MasterServant(String value) {
this.value = value;
}
}
But when I run the repository query I got always 0 results, even there are docs. in the DB:
You need to define your reference type in CouchbaseRepository<T, K> then simply add the reference type Item as CouchbaseRepository<BendicionesDoc<Item>, String> and just use Repository query keywords for findById(String id).
public interface BenRepository extends CouchbaseRepository<BendicionesDoc<Item>, String> {
List<BendicionesDoc<Item>> findById(String id);
}
I inherited some pretty awful code that I am looking to refactor to make more reusable. There is a set of reporting tables which are primarily composed of 3 columns: id, report_type_fk, and report_description. I would like to merge all the reporting tables into one for ease of use.
I am refactoring the code and think that it would be better to break our current entities up so that Report is an abstract class with type implementations. For example a DmvReport extends Report, CreditScoreReport extends Report, etc.
The problem I am running into is that there would only be 1 report table that all entities would need to save to. Is there a way to make all concrete implementations of the abstract Report object save into the same table?
Here's an example of the bad code I inherited
Report class
#Entity
#Table(name = "report")
public class Report<E extends Exception> {
private long id;
private ReportType type;
private String description;
...
...
}
CreditReport class
#Entity
#Table(name = "credit_report")
public class CreditScore Report<E extends Exception> extends Report<E> {
private long id;
private ReportType type;
private String description;
...
...
}
I am looking to turn it into:
#MappedSuperclass
#Table(name = "report")
public abstract class Report<E extends Exception> {
#Id #Column(name="id")
private long id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "report_type_id")
private ReportType type;
#column(name="description")
private String description;
...
...
}
#Entity
#Table(name = "report")
public class CreditScoreReport<E extends Exception> extends Report<E> {
public void doCreditScoreStuff(){
...
}
}
#Entity
#Table(name = "report")
public class DmvReport<E extends Exception> extends Report<E> {
public void doDmvStuff(){
...
}
}
I think you should use #Inheritance instead of #MappedSuperClass. Your code would look like this:
#Entity
#Table(name = "report")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "report_type_id", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Report<E extends Exception> {
#Id #Column(name="id")
private long id;
#column(name="description")
private String description;
...
...
}
#Entity(name = "CreditScoreReport")
#DiscriminatorValue("1") // the id corresponding to the credit score report
public class CreditScoreReport<E extends Exception> extends Report<E> {
#Column(name = "specific_credit_score_report_1)
private Integer specificCreditScoreReport1;
public void doCreditScoreStuff(){
...
}
}
#Entity(name = "DmvReport")
#DiscriminatorValue("2") // the id corresponding to the DMV report
public class DmvReport<E extends Exception> extends Report<E> {
#Column(name = "specific_dmv_score_report_1)
private Integer specificDmvScoreReport1;
public void doDmvStuff(){
...
}
}
This strategy allows you to store credit score report and DMV report data in one table (report), but instanciate the proper entity according to the report_value_id field. You don't have to define the report_value_id in your parameters because it was already used to create the required entity.
Is this what you're looking for?
I would like to receive all of the records, which extend from my abstract class. I have the following:
Product.java
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#DiscriminatorColumn(name="descriminatorColumn")
#Table(name="ProductCatalog")
public abstract class Product {
#Id
private Long id;
}
PC.java
#Entity
#Table(name = "PC")
public class PC extends Product{
private String pcType;
}
TV.java
#Entity
#Table(name = "TV")
public class TV extends Product{
private String tvType;
}
And ProductRepository.java
public interface ProductRepository extends CrudRepository<Product, Long> {
<T extends Product>List<T> findAll(); // not working
}
In my controller I have:
#RequestMapping(value = "/product", method = GET)
public <T extends Product>List<T> findProducts(){
return productRepository.findAll();
}
How can I make the findAll() return all of the items from the subclasses that extend class Product?
UPDATE:
I have added the following method to the ProductRepository:
<T extends Product>List<T> findProducts();
and changed it in the controller - is this the proper way to do it ?
The error that I've using this is:
Caused by:
org.springframework.data.mapping.PropertyReferenceException: No
property findProducts found for type Product!
I do it the following way in my repository. Is a generic answer so you could use it for any class, even your abstract class:
public <T> List<T> findAll(Class<T> type) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> criteria = builder.createQuery(type);
Root<T> root = criteria.from(type);
CriteriaQuery<T> all = criteria.select(root);
TypedQuery<T> allQuery = entityManager.createQuery(all);
return (List<T>) allQuery.getResultList();
}
Edit: False remark removed.
i have this classes
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "processType")
public abstract class BaseProcess extends BaseModel {
}
#Entity
public class MainProcess extends BaseProcess {
}
#Entity
public class SubProcess extends BaseProcess {
}
#Entity
public class Tank {
#Column
private Long id;
}
#Entity
public class TagAddress {
#Id
private Long id
#ManyToOne
private BaseProcess process;
#ManyToOne
private Tank tank;
}
public interface TagAddressDao extends JpaRepository<TagAddress, Long> {
#Query("FROM TagAddress WHERE tank.id = ?1 AND process.processType = 'MainProcess' ")
List<TagAddress> findMainProcessByTankId(Long tankId);
#Query("FROM TagAddress WHERE tank.id = ?1 AND process.processType = 'SubProcess' ")
List<TagAddress> findSubProcessByTankId(Long tankId);
}
i got this error
org.hibernate.QueryException: could not resolve property: processType ...
How do i filter by concrete class MainProcess and SubProcess in TagAddressDao?
something like i can do "instanceof MainProcess" or "instanceof SubProcess"
I have a problem with a QueryDSL query. Classes:
#Entity
#Table(name="project")
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Project extends DomainObject implements Comparable<Project>, IconizedComponent, Commentable {
#ManyToMany(targetEntity=Student.class)
#JoinTable(name="project_student")
#Sort(type=SortType.NATURAL) //Required by hibernate
#QueryInit({"user"})
private SortedSet<Student> projectParticipants = new TreeSet<Student>();
private Project(){}
//attributes, get+set methods etc
}
#Entity
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) //Hibernate specific
public class Student extends Role {
public Student(){}
//attributes, get+set methods etc
}
#Entity
#DiscriminatorColumn(name = "rolename", discriminatorType = DiscriminatorType.STRING, length = 8)
#Table(name="role", uniqueConstraints={#UniqueConstraint(columnNames={"user_id","rolename"}, name = "role_is_unique")})
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class Role extends LazyDeletableDomainObject implements Comparable<Role> {
#ManyToOne(optional=false)
protected User user;
public Role(){}
//attributes, get+set methods etc
}
#Entity
#Table(name="user")
#Cacheable(true)
#Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) //Hibernate specific
public class User extends LazyDeletableDomainObject implements Comparable<User>, IconizedComponent {
private String firstName;
private String lastName;
public User(){}
//attributes, get+set methods etc
}
Query:
private BooleanExpression authorsNameContains(String searchTerm){
QUser user = new QUser("user");
user.firstName.containsIgnoreCase(searchTerm).or(user.lastName.contains(searchTerm));
QStudent student = new QStudent("student");
student.user.eq(user);
return QProject.project.projectParticipants.contains(student);
//java.lang.IllegalArgumentException: Undeclared path 'student'. Add this path as a source to the query to be able to reference it.
}
I have also tried annotating the projectParticipants set in Project with
#QueryInit("*.*")
But that gives the same exception. Any hints?
#Timo Westkämper
#siebZ0r
Thanks for your attention. Sorry for the delayed reply and incorrectly phrased question. Actually what I wanted to do was to write a working BooleanExpression.
In combination with the annotations already made, this was what I was after:
private BooleanExpression authorsFirstNameContains(String searchTerm){
return QProject.project.projectParticipants.any().user.firstName.containsIgnoreCase(searchTerm);
}
I got this right with the help of a colleague.