I have following JPA entity:
Entity
#Entity
#Table(name = "todo")
#NamedEntityGraph(name = "TodoEntity.children",
attributeNodes = #NamedAttributeNode("children"))
public class TodoEntity {
public TodoEntity() {
}
#Id
#GeneratedValue
private Long id;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Long userId;
private String text;
private String description;
private Boolean collapsed;
private Boolean completed;
private Integer sortOrder;
private Date expiredDate;
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
private Date creationDate;
private Integer priority;
private String guid;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
#JsonIgnore
#Column(updatable = false,nullable = false)
private Long parentId;
#OneToMany(fetch = FetchType.EAGER)
#Cascade({CascadeType.ALL})
#Fetch(FetchMode.JOIN)
#JoinColumn(name = "parentId", referencedColumnName = "id")
#OrderBy("sortOrder ASC")
private List<TodoEntity> children;
}
Repo
#EntityGraph(value = "TodoEntity.children", type = EntityGraph.EntityGraphType.FETCH)
Page<TodoEntity> findByUserIdAndParentId(Long userId, Long parentId, Pageable pageable);
I want to have a TodoList that I can infinitely nest.
The code is working so far - only thing is that when I have around 1500 todos and they are nested, SQL and the queries are becoming slow (around 1,3s)
How can I improve this?
IDEA:
Actually I think it would be enough to just get all Todos in the database with one query (that takes 2,2 ms) and then nesting them with java (having the parentId) so that the workload is on the application layer.
Related
I am running following query using Spring JPA in my project. Its internally using Hibernate for connecting to MySql DB. This query is written inside JpaRepository.
#Query(value = "SELECT ipd.* FROM identifier_pool_definition ipd, identifier_definition id WHERE\n" +
"ipd.definition_id = id.definition_id AND id.acquirer_id = :acquirerId AND" +
" id.domain = :domain AND id.definition_type = 'pool' AND id.status IN :statuses AND id.type = :poolType AND id.is_deleted = false",
nativeQuery = true)
List<IdentifierPoolDefinitionEntity> findAllWithPoolTypeAndStatuses(#Param("acquirerId") String processorId,
#Param("domain") String domain,
#Param("poolType") String poolType,
#Param("statuses") Collection<String> statuses);
In the application logs I am observing that after running above query, Hibernate is making individual select DB calls by id for each record fetched in the above query.
Sample logs:
Hibernate:
SELECT
ipd.*
FROM
identifier_pool_definition ipd,
identifier_definition id
WHERE
ipd.definition_id = id.definition_id
AND id.acquirer_id = ?
AND id.domain = ?
AND id.definition_type = 'pool'
AND id.status IN (
?, ?, ?
)
AND id.type = ?
AND id.is_deleted = false
Hibernate:
select
identifier0_.definition_id as definiti1_2_0_,
identifier0_.acquirer_id as acquirer2_2_0_,
identifier0_.created as created3_2_0_,
identifier0_.created_by as created_4_2_0_,
identifier0_.definition_type as definiti5_2_0_,
identifier0_.domain as domain6_2_0_,
identifier0_.is_deleted as is_delet7_2_0_,
identifier0_.merchant_id as merchant8_2_0_,
identifier0_.processor_id as processo9_2_0_,
identifier0_.status as status10_2_0_,
identifier0_.type as type11_2_0_,
identifier0_.updated as updated12_2_0_,
identifier0_.updated_by as updated13_2_0_
from
identifier_definition identifier0_
where
identifier0_.definition_id=?
...
...
after n records
...
...
Hibernate:
select
identifier0_.definition_id as definiti1_2_0_,
identifier0_.acquirer_id as acquirer2_2_0_,
identifier0_.created as created3_2_0_,
identifier0_.created_by as created_4_2_0_,
identifier0_.definition_type as definiti5_2_0_,
identifier0_.domain as domain6_2_0_,
identifier0_.is_deleted as is_delet7_2_0_,
identifier0_.merchant_id as merchant8_2_0_,
identifier0_.processor_id as processo9_2_0_,
identifier0_.status as status10_2_0_,
identifier0_.type as type11_2_0_,
identifier0_.updated as updated12_2_0_,
identifier0_.updated_by as updated13_2_0_
from
identifier_definition identifier0_
where
identifier0_.definition_id=?
Following are my entity classes:
IdentifierPoolDefinitionEntity.java
#Entity
#Table(name = "identifier_pool_definition")
#Getter
#Setter
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class IdentifierPoolDefinitionEntity implements Serializable {
private static final long serialVersionUID = 518449602683891708L;
#Id
#Column(name = "definition_id", columnDefinition = "BINARY(16)")
private UUID definitionId;
#Column(name = "prefix")
private String prefix;
#Column(name = "suffix")
private String suffix;
#Column(name = "formatter")
private String formatter;
#Column(name = "lower_bound")
private Long lowerBound;
#Column(name = "upper_bound")
private Long upperBound;
#Column(name = "`separator`")
private String separator;
#Column(name = "created")
#CreationTimestamp
#JsonSerialize(using = CustomLocalDateTimeSerializer.class)
private LocalDateTime created;
#Column(name = "created_by")
private String createdBy;
#Column(name = "updated")
#JsonSerialize(using = CustomLocalDateTimeSerializer.class)
private LocalDateTime updated;
#Column(name = "updated_by")
private String updatedBy;
#JsonIgnore
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
private IdentifierDefinitionEntity identifierDefinitionEntity;
}
IdentifierDefinitionEntity.java
#Entity
#Table(name = "identifier_definition")
#Getter
#Setter
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class IdentifierDefinitionEntity implements Serializable {
private static final long serialVersionUID = 7809377866509417398L;
#Id
#Column(name = "definition_id", columnDefinition = "BINARY(16)")
private UUID definitionId;
#Column(name = "definition_type")
private String definitionType;
#Column(name = "type")
private String type;
#Column(name = "acquirer_id")
private String acquirerId;
#Column(name = "domain")
private String domain;
#Column(name = "processor_id")
private String processorId;
#Column(name = "merchant_id")
private String merchantId;
#Column(name = "status")
private String status;
#Column(name = "is_deleted")
private boolean isDeleted;
#Column(name = "created")
#CreationTimestamp
#JsonSerialize(using = CustomLocalDateTimeSerializer.class)
private LocalDateTime created;
#Column(name = "created_by")
private String createdBy;
#Column(name = "updated")
#JsonSerialize(using = CustomLocalDateTimeSerializer.class)
private LocalDateTime updated;
#Column(name = "updated_by")
private String updatedBy;
#JsonIgnore
#OneToOne(mappedBy = "identifierDefinitionEntity", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private IdentifierPoolDefinitionEntity identifierPoolDefinitionEntity;
#JsonIgnore
#OneToMany(mappedBy = "identifierDefinitionEntity", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<IdentifierListValuesEntity> listValues;
#JsonIgnore
#OneToMany(mappedBy = "identifierDefinitionEntity", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<IdentifierAssignmentEntity> identifierAssignmentEntity;
}
Driver code:
val allPoolsForProcessor = identifierPoolDefinitionRepository.findAllWithPoolTypeAndStatuses(acquirerId, domain,
listType.getValue(), Arrays.stream(PoolStatus.values())
.map(PoolStatus::getValue)
.collect(Collectors.toList()));
I want to understand why Hibernate is showing this behaviour? Is there a way to restrict the implicit DB calls done by Hibernate?
I think it's something related to this topic : How can I make a JPA OneToOne relation lazy
Try to make the relation explicitly not-nullable and lazy so that hibernate can knows if it can makes a proxy or have to get the real entity for the field identifierDefinitionEntity on IdentifierPoolDefinitionEntity :
#OneToOne(optional = false, fetch = FetchType.LAZY)
I have created JPA entities class. But using basic query taking humongous amount of time for basic sql query having 100 records in database.
I have 4 tables. (Group/GroupA/GroupB/GroupC) having below structure.
#Table(name = "group")
public class Group implements Serializable {
#Id
#Column(name = "id")
private String id;
#Column(name = "publish_date")
private LocalDate publishDate;
#OneToOne(mappedBy = "group", cascade = CascadeType.ALL)
private GroupA groupA;
#OneToOne(mappedBy = "group", cascade = CascadeType.ALL)
private GroupB groupB;
#OneToOne(mappedBy = "group", cascade = CascadeType.ALL)
private GroupC groupC;
}
#Table(name = "groupA")
public class GroupA {
#Id
#Column(name = "group_id")
private Long groupID;
#Column(name = "name")
private String name;
#OneToOne
#JoinColumn(name = "id")
#JsonIgnore
private Group Group;
}
#Table(name = "groupB")
public class GroupB {
#Id
#Column(name = "group_id")
private Long groupID;
#Column(name = "name")
private String name;
#OneToOne
#JoinColumn(name = "id")
#JsonIgnore
private Group Group;
}
#Table(name = "groupC")
public class GroupC {
#Id
#Column(name = "group_id")
private Long groupID;
#Column(name = "name")
private String name;
#OneToOne
#JoinColumn(name = "id")
#JsonIgnore
private Group Group;
}
Repository class:
#Repository
public interface GroupRepository extends JpaRepository<Group, Long> {
#Query("select r from Group r WHERE r.publishDate = ?1")
List<Group> findAllGroupForDate(LocalDate businessDate);
}
The above basic query is taking 1 minute 30 seconds for around 100 rows in Main Group table.
Please help if I am making anything wrong in entity class.
THanks.
Try to use the Lazy loading concept.
#OneToOne(mappedBy = "group", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
I am having a faq entity as below. Here createdBy field is having a manyToOne relationship with the user entity. Below joinColumns shows the association.
In the User entity, i have OneToMany relationship with UserRoles and UsersUnit which is EAGER load for User and not for faq. So i added #JsonIgnoreProperties
for UsersUnit and UsersRole and the corresponding User entity is shown below.
#Entity
#Table(name = "FAQ", catalog="abc")
public class Faq implements Serializable {
public Faq() {
super();
}
#Column(name = "CREATE_DATE")
private Timestamp createDate;
#Where(clause = "DELETE_DATE is null")
#Column(name = "DELETE_DATE")
private Timestamp deleteDate;
#Column(name = "DELETED_BY")
private BigDecimal deletedBy;
#Column(name = "DOC_BLOB", nullable = false)
#JsonIgnore
private byte[] docBlob;
#Column(name = "DOC_NAME", nullable = false, length = 100)
private String docName;
#Id
private BigDecimal id;
#Column(name = "ORDER_BY")
private BigDecimal orderBy;
#Column(name = "UPDATE_DATE")
private Timestamp updateDate;
#Column(name = "UPDATED_BY")
private BigDecimal updatedBy;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name="created_by", referencedColumnName="id")
})
private User faqCreatedBy;
}
User entity:
#Entity
#Table(name = "USERS", catalog="abc")
#JsonInclude(Include.NON_NULL)
public class User extends EntityLog{
private BigDecimal id;
private BigDecimal edipi;
private String firstname;
private String lastname;
private String email;
..///
private Set<UsersRoles> userRoles;
private Set<UsersUnit> usersUnit;
#Id
#Column(name="id")
public BigDecimal getId() {
return id;
}
...///
#Column
#JsonIgnoreProperties({ "faqCreatedBy" })
#OneToMany(fetch = FetchType.EAGER,mappedBy = "user",cascade = {CascadeType.ALL})
#JsonManagedReference
public Set<UsersRoles> getUserRoles() {
return userRoles;
}
...///
#Column
#JsonIgnoreProperties({ "faqCreatedBy" })
#OneToMany(fetch = FetchType.EAGER,mappedBy = "user",cascade = {CascadeType.ALL})
#JsonManagedReference
public Set<UsersUnit> getUsersUnit() {
return usersUnit;
}
////...
}
With this change I am expecting the faq to load with User entity but I am not execting UsersRoles and UsersUnit to load.
But that is not what i see. When faq loads it loads User and UsersRoles and UsersUnit. I am using Spring JPA fyi. Any leads what is wrong ? Appreciate any inputs.
I had entity for JSON parsing
#Entity
public class Product{
private int productId;
private String productName;
private BigDecimal productPrice;
private int productVendorId;
private String productVendorName;
private int productCategoryId;
private String productCategoryName;
//getters setters here
created 3 tables in dataBase:
products (product_id, product_name,product_price, product_vendor_id), product_category_id);
vendors(vendor_id, vendor_name); categories (category_id, category_name);
in 1st table product_vendor_id fk -> vendor_id pk in vendors and product_category_id fk -> category_id pk in categories
i tried something like this:
#Entity
#Table(name = "products, schema = "market")
public class Product
#Id
#Column(updatable = false, nullable = false, name = "product_id")
private int Id;
#Column(name = "product_name")
private String productName;
#Column(name = "product_price")
private BigDecimal productPrice;
#Column(name = "product_vendor_id")
private int productVendorId;
#Columnt(table = "vendors", name = "vendor_name")
private String vendor_name;
#Column(name = "product_category_id")
private int productCategoryId;
#Column(table = "categories", name = "category_name")
private String productCategorName;
//getters setters here
received alot of errors: like i have not category_name column in products table etc. this error i received when used
#Table(name = "products", schema = "market" )
#SecondaryTables({#SecondaryTable(name = "vendors", schema = "market"),
#SecondaryTable(name = "categories", schema = "market")})
#JsonIgnoreProperties(ignoreUnknown = true)
public class Product {
....
#JoinColumn(name = "product_vendor_id", referencedColumnName = "vendor_id")
private int productVendorID;
#JoinColumn(table = "vendors", name = "vendor_name")
private String productVendorName;
#JoinColumn(name = "product_category_id", referencedColumnName =
"product_category_id")
private int productCategoryID;
#JoinColumn(table = "categories", name = "category_name")
private String productCategoryName;
exception:
Caused by: org.postgresql.util.PSQLException: ERROR: column
product0_1_.product_id doesn't exist
Hint: There may have been a link to the "product0_.product_id" column
Position: 705
how can i map this entity on 3 tables?
upd: i don't want separate this entity, i need this for deserialize my json object too, just want reuse this entity on different operations.
example of json
{"productID":"1111111","productName":"Cool product","productPrice":"99.99","productVendorName":"Some store","productVendorID":"1337","productCategoryName":"Food","productCategoryID":"1"}
Since there are 3 separate tables, you would want to create three separate entity classes.
Also I'm assuming vendors and category tables will have one to many relation to product.
Try below code:
Product:
#Entity
public class Product {
#Id
private int productId;
private String productName;
private BigDecimal productPrice;
private String productVendorName;
private String productCategoryName;
#ManyToOne
#JoinColumn(name = "productCategoryId")
private Category category;
#ManyToOne
#JoinColumn(name = "productVendorId")
private Vendors vendor;
}
Category:
#Entity
public class Category {
#Id
private Integer categoryId;
private String categoryName;
#OneToMany(mappedBy = "category", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
#NotEmpty
private List<Product> products = new ArrayList<>();
}
Vendors:
#Entity
public class Vendors {
#Id
private int vendorId;
private String vendorName;
#OneToMany(mappedBy = "vendor", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
#NotEmpty
private List<Product> products = new ArrayList<>();
}
Though, I would recommend using above approach, if you still want to have single entity class and 3 separate table with redudant data then use below:
#Entity
#SecondaryTables({ #SecondaryTable(name = "vendors"), #SecondaryTable(name = "categories") })
public class Product {
#Id
private int productId;
private String productName;
private BigDecimal productPrice;
private String productVendorName;
private String productCategoryName;
#Column(table = "categories")
private Integer categoryId;
#Column(table = "categories")
private String categoryName;
#Column(table = "vendors")
private int vendorId;
#Column(table = "vendors")
private String vendorName;
}
The id column of the main table will be present in all the 3 tables and used for joining them.
Sorry for poor wording of the question, just didn't know how to explane what i wanted.
All what i need just add #transient annotations for fields which i don't have in products table, and separate it like accepted answer was suggested.
#Entity
#Table(name = "products", schema = "store" )
#JsonIgnoreProperties(ignoreUnknown = true)
public class Product {
#Id
#Column(updatable = false, nullable = false, name = "product_id")
private int productId;
#Column(name = "product_name")
private String productName;
#Column(name = "product_price")
private BigDecimal productPrice;
#Transient
private String productVendorName;
#Transient
private String productCategoryName;
#Transient
private int vendorId;
#Transient
private int categoryId;
#ManyToOne
#JoinColumn(name = "product_category_id")
private Category category;
#ManyToOne
#JoinColumn(name = "product_vendor_id")
private Vendor vendor;
}
for vendors table entity
#Entity
#Table(name = "vendors", schema = "store")
public class Vendor {
#Id
#Column(name = "vendor_id")
private int vendorId;
#Column(name = "vendor_name")
private String vendorName;
#OneToMany(mappedBy = "vendor", cascade = CascadeType.ALL, fetch = FetchType.LAZY,
orphanRemoval = true)
#NotNull
private List<Product> products = new ArrayList<>();
}
and for categories
#Entity
#Table(name = "categories", schema = "store")
public class Category {
#Id
#Column(name = "category_id")
private Integer categoryId;
#Column(name = "category_name")
private String categoryName;
#OneToMany(mappedBy = "category", cascade = CascadeType.ALL, fetch = FetchType.LAZY,
orphanRemoval = true)
#NotNull
private List<Product> products = new ArrayList<>();
}
Wanted to leave here full answer on my question, maybe someone will need it later
Just check some problems with toString. Use it only in Product.class and better make 2 versions for print json and jpa.
I'm trying to build a multi-language database, so I've used this database design as a approach for mine.
Now I've two problems/questions:
I want to retrieve all LocalizedEvent for a given language and given categoryId. How can I make a inner join over the LocalizedCategory table with Hibernate Criteria API?
With SQL I would make this statement to get all LocalizedEvent + LocalizedCategory:
SELECT * FROM event e
INNER JOIN
localized_event le ON (le.event_id = e.event_id)
INNER JOIN
localized_category lc ON (lc.category_id = e.category_id)
WHERE
le.locale = 'de' AND lc.locale = 'de'
My current approach looks like this without getting the LocalizedCategory (with Criteria API):
Criteria c = session.createCriteria(LocalizedEvent.class, "localizedEvent");
c.createAlias("localizedEvent.event", "event");
c.createAlias("event.category", "category");
c.add(Restrictions.eq("category.categoryId", categoryId));
c.add(Restrictions.eq("localizedEvent.locale", language));
I think my mapping is not 100% correct. The entity LocalizedEvent should have a property localizedCategory, but I don't want to save the ID of this localizedCategory (therefore I'm using the #Transient annotation) in the LocalizedEvent table, e.g. using a ManyToOne relation (joining LOC_CATEGORY_ID). But I think it's not possible to do this, isn't it? I would have to map this transient field to LocalizedEvent "manually", because Hibernate is not supporting this mapping (if I'm right).
(Using JDBC this property/mapping would cause no problems, because I can easily make my inner joins and assign the property localizedCategory to the LocalizedEvent in a RowMapper or so).
My entities looks like this:
Event
#Entity
#Table(name = "EVENT")
public class Event {
#Id
#GeneratedValue
#Column(name = "EVENT_ID", unique = true)
private Long eventId;
#Column(name = "DATE")
private Date date;
#OneToMany(mappedBy = "event")
private Set<LocalizedEvent> localizedEvents;
#ManyToOne
#JoinColumn(name = "CATEGORY_ID")
private Category category;
}
LocalizedEvent
#Entity
#Table(name = "LOCALIZED_EVENT")
public class LocalizedEvent {
#Id
#GeneratedValue
#Column(name = "LOC_EVENT_ID")
private Long locEventId;
#ManyToOne
#JoinColumn(name = "EVENT_ID")
private Event event;
#Transient
private LocalizedCategory localizedCategory;
#Column(name = "DESCRIPTION")
private String description;
#Column(name = "LOCALE")
private String locale;
}
Category
#Entity
#Table(name = "CATEGORY")
public class Category {
#Id
#GeneratedValue
#Column(name = "CATEGORY_ID", unique = true)
private Long categoryId;
#OneToMany(mappedBy = "category")
private Set<LocalizedCategory> localizedCategories;
#OneToMany(mappedBy = "category")
private Set<Event> events;
}
LocalizedCategory
#Entity
#Table(name = "LOCALIZED_CATEGORY")
public class LocalizedCategory {
#Id
#GeneratedValue
#Column(name = "LOC_CATEGORY_ID")
private Long locCategoryId;
#ManyToOne
#JoinColumn(name = "CATEGORY_ID")
private Category category;
#Column(name = "NAME")
private String name;
#Column(name = "LOCALE")
private String locale;
}