Using JPARepository on Entity with Inheritancetype.TABLE_PER_CLASS result in wrong query - spring

I've defined 2 entities in my project using the inheritance type "TABLE_PER_CLASS".
After that I've defined 2 repositories to access data but, when I use them to find records for the B entity, the query generated does not include a JOIN statement but only "SELECT id, name, alternate_name from b" and it fails because "name" field does not exists.
Did I miss something that I can't see?
Entity A:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Table(name = "a")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public abstract class A implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "name")
private String name;
...
...
}
Entity B:
#Entity
#Table(name = "b")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class B extends A implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "alternate_name")
private String alternateName;
...
...
}
BaseRepository:
#SuppressWarnings("unused")
#NoRepositoryBean
public interface BaseRepository<T extends A> extends JpaRepository<T, Long> {
}
ARepository:
#SuppressWarnings("unused")
#Repository
public interface ARepository extends BaseRepository<A> {
}
BRepository:
#SuppressWarnings("unused")
#Repository
public interface BRepository extends BaseRepository<B> {
}

I assume that you have a table a and a table b.
But TABLE_PER_CLASS will only generate/use a table per concrete subclass. In your case just table b.
If you want table a and b you must use the JOINED strategy.

Try to use #GeneratedValue annotation without explicitly saying the strategy.
#Id
#GeneratedValue
private Long id;

Related

How to implements entity with 2 entity as primary key with jpa annotation and repository

i want to implement a many to many association with quantity information in it . like this :
#Entity
#Table(name = "reserves")
#Getter #Setter #NoArgsConstructor
public class Reserve {
#Id
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "groupe_id")
private GroupeSanguin bloodGroup;
#Id
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Banque banque;
private int quantity;
}
the GroupSanguin and the Banque are two class stored in the database two . here is the code for the two if you need :
#Entity
#Table(name = "groupe_sanguins")
public class GroupeSanguin {
#Id
private String groupe;
#OneToMany(mappedBy = "groupeSanguin")
private List<Donneur> donneurs;
}
#Entity #Getter #Setter #NoArgsConstructor
public class Banque {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true,nullable = false)
private String nom;
private String adresse;
#Column(unique = true)
private String telephone;
private String localisation;
}
so my i want to know how to annotate the JpaRepository to take the two as primary key like this and is my annotation good for it to work ?
public interface ReserveRepository extends JpaRepository<
Reserve,
//what to put here ?
>
This isn't a JPA question in fact, it's a relationnal database conception.
If Reserve has is own data and links with other entity it has it own Id
You can add unicity constraint
#Entity
#Table(name = "reserves", uniqueConstraints={
#UniqueConstraint(columnNames = {"banque_id", "groupe_id"})
#Getter #Setter #NoArgsConstructor
public class Reserve {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "groupe_id")
private GroupeSanguin bloodGroup;
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "banque_id")
private Banque banque;
private int quantity;
}
i've found this solutions too.
#Entity
#Table(name = "reserves")
#Getter #Setter #NoArgsConstructor
#IdClass(ReserveId.class) //this annotation will tell that id that the
// the id will be represented by a class
public class Reserve {
#Id
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "groupe_id")
private GroupeSanguin groupeSanguin;
#Id
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "banque_id")
private Banque banque;
private int quantity;
}
and the id class should implements Serializable like this :
#Getter #Setter
public class ReserveId implements Serializable {
private Banque banque;
private GroupeSanguin groupeSanguin;
}
and finally the repository will be like that :
#Repository
public interface ReserveRepo extends JpaRepository<Reserve, ReserveId>{}
See your Reserve class has nowhere mentioned composite primary key. First you need to fix the model, You can refer to the solution here How to create and handle composite primary key in JPA

Fetching all entities with many to many relationships in repository layer : Hibernate with JPA

in my current project, I am using Hibernate with JPA.
I have entities as follows:
Entity A:
#Entity
#Table(name = "a")
public class A implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
#Column(name = "name")
private String name;
#OneToMany
private Set<Association> associations = new HashSet<>();
}
Entity B:
#Entity
#Table(name = "b")
public class B implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
#Column(name = "description")
private String description;
#OneToMany
private Set<Association> associations = new HashSet<>();
}
Entity Association:
#Entity
#Table(name = "association")
public class Association implements Serializable {
private static final long serialVersionUID = 1L;
#ManyToOne
#JoinColumn(name = "a_id", referencedColumnName = "id")
A a;
#ManyToOne
#JoinColumn(name = "b_id", referencedColumnName = "id")
B b;
...other columns..
}
Now I would like to implement repository layer for A as follows:
#Repository
public interface ARepository extends JpaRepository<A, Long> {
//Here I would like to write the function/query get all the As with many-to-many relationships.
}

JPA Composite PK referenced by FKs in One to Many Relationship

I am trying to map the tables below into JPA. The relationships are one-to-many between user_tax and tax and user_tax and user. It has confused me the fact that i have a composite primary key, and i need to map the foreign keys to these 2 keys.
the error message: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: entity.Tax.user_tax in entity.UserTax.taxs
tax user_tax user
-------- -------- ------
PK|t_id |--------| t_id |PK-FK |u_name|
|t_name| PK-FK| u_id |-------|u_id | PK
| | | name | | |
Here is my Entities:
#Entity
#Table(name = "user")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="u_name")
private String uname;
getters + setters
}
#Entity
#Table(name = "tax")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Tax implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "t_name")
private String tname;
#Embeddable
public class UserTaxId implements Serializable {
#Column(name="u_id")
private Long uId;
#Column(name="t_id")
private Long tId;
#Entity
#Table(name = "user_tax")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class UserTax implements Serializable {
#EmbeddedId
private UserTaxId userTaxId;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user_tax")
private List<User> users;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user_tax")
private List<Tax> taxs;
Your 1:n mappings are backwards (i.e. a UserTax can have only a single User and a single Tax) and you are using a derived identity. Try mapping UserTax like this:
#Entity
#Table(name = "user_tax")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class UserTax implements Serializable {
#EmbeddedId
private UserTaxId userTaxId;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("uId") // maps uId attribute of embedded id
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("tId") // maps tId attribute of embedded id
private Tax tax;
...
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.
I will post here what worked for me after 3 days of research.
Brian Vosburgh correctly posted the UserTax class:
#Entity
#Table(name = "user_tax")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class UserTax implements Serializable {
#EmbeddedId
private UserTaxId userTaxId;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("uId") // maps uId attribute of embedded id
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("tId") // maps tId attribute of embedded id
private Tax tax;
...
}
However, i was getting error meassages and my code wasn't compiling. Then i also had to edit the User and Tax classes:
#Entity
#Table(name = "user")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(
mappedBy = "tid",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<UserTax> tax = new ArrayList<>();
#Column(name="u_name")
private String uname;
getters + setters
}
#Entity
#Table(name = "tax")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Tax implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(
mappedBy = "uid",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<UserTax> taxs = new ArrayList<>();
#Column(name = "t_name")
private String tname;
setters+getters
}
Here is the link where i found the solution to my problem: https://vladmihalcea.com/the-best-way-to-map-a-many-to-many-association-with-extra-columns-when-using-jpa-and-hibernate/

Jpa findAllBy* using Long id instead of an entity

I just do not want to do:
myEntity = findById(Long id)
findAllByEntityAnd*(MyEntity myEntity, *)
instead I want do:
findAllByEntityAnd*(Long entityId, *)
Is there something I missed to achieve what I wanted or I just cannot achieve it directly?
Thanks for the help ~
When I tried it, the Spring prompted me:
java.lang.IllegalArgumentException: Could not create query metamodel for method public abstract java.util.List com.worksap.morphling.raptor.dump.thread.dao.ThreadDoRepository.findAllByDumpDoAndLocksWaitingContains(java.lang.Long,java.lang.String)!
Here is my table for your reference:
#Data
#Builder
#Entity
#Table(name = "thread_info")
#AllArgsConstructor
#NoArgsConstructor
public class ThreadDo implements Serializable {
private static final long serialVersionUID = -1L;
String name;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "thread_dump_id")
#JsonIgnore
private ThreadDumpDo dumpDo;
...
}
#Data
#Builder
#Entity
#Table(name = "thread_dump")
#AllArgsConstructor
#NoArgsConstructor
public class ThreadDumpDo implements Serializable {
private static final long serialVersionUID = -1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(
mappedBy = "dumpDo",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<ThreadDo> threadDoList;
}
And then I have a ThreadDoRepository and want to locate the threadDos directly like this:
List<ThreadDo> findAllByDumpDoAndLocksWaitingContains(Long dumpId, String lockWaiting);
I can achieve it via #Query as follows, but I really think it's pretty ugly since it's easy to interpret (I think).
#Query(value = "select * from thread_info t where t.thread_dump_id = ?1 and t.locks_waiting like ?2",
nativeQuery = true)
List<ThreadDo> findAllByDumpDoAndLocksWaitingContains(Long dumpId, String lockWaiting);
Try this
List<ThreadDo> findAllByDumpDo_IdAndLocksWaitingContains(Long dumpId, String lockWaiting);

Spring Data/JPS (N+1) SELECTs with OneToOne associations

In my Spring Data application I ran into (N+1) selects issue.
I have a following Spring Data entities:
#Entity
#Table(name = "card_categories")
public class CardCategory extends BaseEntity implements Serializable {
#Id
#SequenceGenerator(name = "card_categories_id_seq", sequenceName = "card_categories_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.AUTO, generator = "card_categories_id_seq")
private Long id;
private String name;
...
}
#Entity
#Table(name = "levels")
public class Level extends BaseEntity implements Serializable {
#Id
#SequenceGenerator(name = "levels_id_seq", sequenceName = "levels_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.AUTO, generator = "levels_id_seq")
private Long id;
private String name;
...
}
#Entity
#Table(name = "card_categories_levels")
public class CardCategoryLevel extends BaseEntity implements Serializable {
#Id
#SequenceGenerator(name = "card_categories_levels_id_seq", sequenceName = "card_categories_levels_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.AUTO, generator = "card_categories_levels_id_seq")
private Long id;
#OneToOne
#JoinColumn(name = "card_category_id")
private CardCategory cardCategory;
#OneToOne
#JoinColumn(name = "level_id")
private Level level;
...
}
and empty Spring Data repository:
#Repository
public interface CardCategoryLevelRepository extends JpaRepository<CardCategoryLevel, Long> {
}
When I try to fetch all CardCategoryLevel entities by cardCategoryLevelRepository.findAll() method it produces 3 SELECTs for an each row in my card_categories_levels table.
In order to use one single JOIN instead of N+1 SELECTs I have reimplemented my CardCategoryLevelRepository:
#Repository
public interface CardCategoryLevelRepository extends JpaRepository<CardCategoryLevel, Long> {
#Query(value = "SELECT ccl FROM CardCategoryLevel ccl LEFT JOIN FETCH ccl.cardCategory cc LEFT JOIN FETCH ccl.level l where cc = :cardCategory and l = :level")
CardCategoryLevel findByCardCategoryAndLevel(#Param("cardCategory") CardCategory cardCategory, #Param("level") Level level);
#Override
#Query(value = "SELECT ccl FROM CardCategoryLevel ccl LEFT JOIN FETCH ccl.cardCategory LEFT JOIN FETCH ccl.level")
List<CardCategoryLevel> findAll();
}
but I'm not sure I did it in a right optimal way.
Please validate my approach and tell it is optimal workaround for (N+1) SELECTs issue with OneToOne associations in Spring Data or no and what is the best way to solve it.
Should I leave it as is or may be move to some other abstraction.. for example like QueryDSL or something like that ?
Thanks to Xtreme Biker I have re-implemented my solution with entity graphs and QueryDSL

Resources