In Spring Data is it possible to extend a query that is generated by the find* functions of the repo interfaces?
Given following use case:
My legacy database has an inheritance by table. So given following
#Entity public class Person {
private int id;
private String className;
}
#Entity #PrimaryKeyJoinColumn(name="id") public class Musician extends Person {
String instrument;
}
#Entity #PrimaryKeyJoinColumn(name="id") public class Lawyer extends Person {
String discipline;
}
My repository for Musician:
public interface MusicianRepository extends CrudRepository<Musician, int> {
List<Musician> findAll();
}
Now an entry for a new musician in SQL would be:
insert into Person(id, className) values(1, 'Musician');
insert into Musician(id, instrument) values(1, 'piano');
When a Musician got migrated to a lawyer the old system added one row to Lawyer table without removing Musician by:
insert into Lawyer(id, discipline), values(1, 'finance');
update Person set ClassName = 'Lawyer' where ID = 1;
My MusicianRepo would now find the lawyer since the row in Musician still exists.
I would need some kind of post processor where I could extend the query by adding a where clause with "ClassName = 'Musician'" on all find* methods.
Is this possible somehow?
I think that your JPA mapping is just not correct in terms of inheritance.
I think you want to have "Joined, Multiple Table Inheritance"
Citing from here:
Joined inheritance is the most logical inheritance solution because it
mirrors the object model in the data model. In joined inheritance a
table is defined for each class in the inheritance hierarchy to store
only the local attributes of that class. Each table in the hierarchy
must also store the object's id (primary key), which is only defined
in the root class. All classes in the hierarchy must share the same id
attribute. A discriminator column is used to determine which class the
particular row belongs to, each class in the hierarchy defines its own
unique discriminator value.
Some JPA providers support joined inheritance with or without a
discriminator column, some required the discriminator column, and some
do not support the discriminator column. So joined inheritance does
not seem to be fully standardized yet.
The className column in Person would be your descriminator column. It determines the subclass to instantiate.
Your mapping would be something like this:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="className")
public class Person {
private int id;
private String className;
}
#Entity
#DiscriminatorValue("Musician")
public class Musician extends Person {
String instrument;
}
#Entity
#DiscriminatorValue("Lawyer")
public class Lawyer extends Person {
String discipline;
}
This way if you query for Lawyer entities JPA would automatically add the where clause to just read rows with className=Lawyer
I did not try the mapping - it should just illustrate the way you should be going.
Related
I've been doing research lately on inheritance strategies of JPA.I decided to develop a new project and I decided that the most suitable strategy for me in this project is JOINED.My Entity hierarchy is like this:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Account {
#Id
#GeneratedValue
private long id;
private String iban;
}
#Entity
public class DrawingAccount extends Account{
public String drawingInfo;
}
#Entity
public class SavingsAccount extends Account{
private String savingsInfo;
}
When I create a structure in this way, the database structure is created as I want. The common field (like IBAN) of subclasses is kept on the account table.Different fields on subclasses are kept in their own tables.But when I want to fetch only common fields ( like IBAN ) from database (SELECT * FROM ACCOUNT) it is sending a JOIN query to the tables of the subclasses for me.It's nice that it does this, but I only want to see the common areas.I only want the data for the ACCOUNT table in the database. Is there a way around this? I don't want it to send a JOIN query.There is nothing wrong with sending a JOIN, but in some cases like when i need this, it should send a join query. When I don't want it to send a join query, it should not send JOIN.How can I do it?
I am writing Spring Boot Data JPA application and I have following situation:
I have 2 database queries fetching from same table but they are fetching different columns and data based on their WHERE clauses. For example:
SELECT CAR_TYPE, CAR_MODEL, CAR_YEAR, ACCIDENT_YEAR, BUY_DAY FROM CAR WHERE ACCIDENT_YEAR IS NULL
, and
SELECT CAR_MODEL, CAR_YEAR FROM CAR WHERE CAR_YEAR >= CURRENT_YEAR
As you can see these 2 queries (whose results will be exposed through 2 different API points) reference the same table CAR, but return different fields and have different WHERE clauses.
I know in JPA, I have to create an entity CarEntity like:
#Entity
#Table(name = "CAR")
public class CarEntity {
// I can only have fields from one or the other query
// here, so I guess I have to have 2 of these
}
, but my problem is that this entity needs to apply for the 2 different queries (with different fields returned, different data, different WHERE clauses).
So, it looks like I have to have actually 2 CarEntity classes. But, I am not sure how to make these 2 CarEntities so they both reference the same table CAR?
You can do by using projection that basically you define an interface with field methods which you want to get them. Projections
#Entity
public class Car implement CarSummary { // if you want you can implement JIC
private UUID id;
private String carType;
private String carModel;
private LocalDateTime carYear;
//getters and setters
}
public interface CarSummary {
String getCardModel();
String getCarYear();
}
Then on your query.
public interface CarRepository extends Repository<Car, UUID> {
Collection<CarSummary> findByCarYearGreaterThan(LocalDateTime now);
Collection<Car> findByAccidentYearIsNull();
}
I have two entities Estate and PropertyTags in a Spring Boot application. The Estate entity has a many-to-many relationship with the PropertyTag (PropertyTag is also used by other entities)
This is the Estate entity:
#Entity
public class Estate{
#Id
private Long id;
.
.
#ManyToMany
private Set<PropertyTag> propertyTags;
.
.
// other properties
}
And the PropertyTag class:
#Entity
public class PropertyTag{
#Id
private Long id;
private String tagName;
// getters and setters
}
The above relationship created 3 database tables with one table for foreign keys of the relationship.
I need a repository method (or query) that will retrieve an Estate that will take and argument of an estate Id and property tag object.
I tried using the hibernate keywords as below:
public interface EstateRepository extends JpaRepository<Estate, Long> {
Optional<Estate> findByIdAndPropertyTagsContaining(Long estateId, PropertyTag childTag);
}
But that did not work.
I do not want to retrieve an estate via its ID and manually loop through its property tags to check if a tag exists in its collection. I feel this can be done with a query of the database
I am not so good at writing custom queries. I need help with the query to do that.
Thank you.
To get an Estate entity by the PropertyTag entity you can also just use the id of the PropertyTag and try
Optional<Estate> findByIdAndPropertyTags_Id(Long estateId, Long propertyTagId);
Which should return the Estate containing a tag with the given ID.
Containing is used for String searching
I'm using spring-boot-starter-data-jdbc 2.4.2. In my domain aggregate I need to map a List of Strings that is populated from a column in another table. It is a legacy database so I have no control over the table and column names and need to use custom names. I see there is an #MappedCollection annotation, but can't see how to use it in this scenario. Below is my class:
#Data
#Table("NMT_MOVIE_THEATRE")
public class MovieTheatre {
#Id
#Column("MOVIE_THEATRE_ID")
private Long id;
#Column("ZIP_CODE")
private String zipCode;
// this comes from table NMT_CURRENT_MOVIE, column CM_ID, joined by MOVIE_THEATRE_ID
private List<String> currentMovieIds;
}
Using Spring Data JDBC, how can I create the one-to-many relation?
Wrap your String in a little entity.
#Table("NMT_CURRENTMOVIE")
class MovieId {
#Id
#Column("CM_ID")
final String id
// add constructor, equals and hashCode here or generate using Lombok
}
Then use it in the MovieTheatre. Since you don't have a column for an index, the proper collection to use is a Set
// ...
class MovieTheatre {
// ...
#MappedCollection(idColumn="MOVIE_THEATRE_ID")
Set<MovieId> currentMovieIds;
}
Note that equals and hashCode is important as well as the constructor taking all arguments used in those, since the entity is used in a Set.
I'm letting system to create queries from the methods names:
public interface CompanyRepository extends JpaRepository<Company, Long> {
Boolean existsByName(String name);
Boolean existsByRegCode(String regCode);
}
There are several different repos for different entities (Company, User, Shop) and they all have field named CountryId.
Now I need to add condition "AND CountryId = :CountryId" to all queries in all repos , where the country parameter gets it's value from some configuration file.
I know that I should build some base class for this and extend from that, but can't figure out what to put into this class.
You can define a superclass for all of your entities and add the countryId field to this superclass and annotate with #Where(clause = "countryId='id'")
#Where(clause = "countryId='id'")
public class Entity {...}
and
public class Company extends Entity {...}