I tried to use entity graph to avoid N+1 query, and it work as expected.
It is different in self reference entity, though these code could get correct list, the N+1 query occurred.
My question is how to eliminate N+1 query with self reference entity?
Thanks in advance.
log and code as following
Hibernate:
select
sysperm0_.pval as pval1_0_0_,
children1_.pval as pval1_0_1_,
sysperm2_.pval as pval1_0_2_,
sysperm0_.parent as parent4_0_0_,
sysperm0_.created as created2_0_0_,
sysperm0_.leaf as leaf3_0_0_,
sysperm0_.pname as pname5_0_0_,
sysperm0_.ptype as ptype6_0_0_,
sysperm0_.updated as updated7_0_0_,
children1_.parent as parent4_0_1_,
children1_.created as created2_0_1_,
children1_.leaf as leaf3_0_1_,
children1_.pname as pname5_0_1_,
children1_.ptype as ptype6_0_1_,
children1_.updated as updated7_0_1_,
children1_.parent as parent4_0_0__,
children1_.pval as pval1_0_0__,
sysperm2_.parent as parent4_0_2_,
sysperm2_.created as created2_0_2_,
sysperm2_.leaf as leaf3_0_2_,
sysperm2_.pname as pname5_0_2_,
sysperm2_.ptype as ptype6_0_2_,
sysperm2_.updated as updated7_0_2_
from
sys_perm sysperm0_
left outer join
sys_perm children1_
on sysperm0_.pval=children1_.parent
left outer join
sys_perm sysperm2_
on children1_.parent=sysperm2_.pval
where
sysperm0_.pval=?
Hibernate:
select
children0_.parent as parent4_0_0_,
children0_.pval as pval1_0_0_,
children0_.pval as pval1_0_1_,
children0_.parent as parent4_0_1_,
children0_.created as created2_0_1_,
children0_.leaf as leaf3_0_1_,
children0_.pname as pname5_0_1_,
children0_.ptype as ptype6_0_1_,
children0_.updated as updated7_0_1_
from
sys_perm children0_
where
children0_.parent=?
.......XN
492373 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
5197227 nanoseconds spent preparing 8 JDBC statements;
18997333 nanoseconds spent executing 8 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
the entity
@Table(name="sys_perm")
@Entity
@NamedEntityGraph(
name = "test",
attributeNodes = {
@NamedAttributeNode(value="children",subgraph="sub_perm"),
},
subgraphs = {
@NamedSubgraph(
name = "sub_perm",
attributeNodes = {
@NamedAttributeNode("_parent")
}
)
}
)
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class SysPerm implements Serializable {
@Id
private String pval;
private String parent;
private String pname;
private Integer ptype;
private Boolean leaf;
@CreationTimestamp
private Date created;
@UpdateTimestamp
private Date updated;
@ManyToOne(fetch=FetchType.LAZY, cascade={CascadeType.ALL})
@JoinColumn(name = "parent", referencedColumnName = "pval", insertable=false, updatable=false)
@JsonIgnore
private SysPerm _parent;
@OneToMany(mappedBy="_parent", fetch=FetchType.EAGER, cascade={CascadeType.ALL})
private List<SysPerm> children = new ArrayList<>();
}
the repository
public interface SysPermRepository extends JpaRepository<SysPerm, Long>{
@EntityGraph(value = "test", type = EntityGraphType.FETCH)
List<SysPerm> findByPval(String pval);
}
the schema
CREATE TABLE `sys_perm` (
`pval` varchar(50) NOT NULL ,
`parent` varchar(25) DEFAULT NULL ,
`pname` varchar(50) DEFAULT NULL ,
`ptype` int(3) DEFAULT NULL ,
`leaf` tinyint(1) DEFAULT NULL ,
`created` timestamp NULL DEFAULT NULL ,
`updated` timestamp NULL DEFAULT NULL ,
PRIMARY KEY (`pval`),
UNIQUE KEY `pval` (`pval`),
KEY `FKaiy87e3krvn4suwleaooces17` (`parent`),
CONSTRAINT `FKaiy87e3krvn4suwleaooces17` FOREIGN KEY (`parent`) REFERENCES `sys_perm` (`pval`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
The problem will be resolved by changing EntityGraphType.FETCH
to EntityGraphType.LOAD
public interface SysPermRepository extends JpaRepository<SysPerm, Long>{
@EntityGraph(value = "test", type = EntityGraphType.LOAD)
List<SysPerm> findByPval(String pval);
}
My problem was resolved by this way, you can try it.
I had the same problem and resolved with this post about alternatives for querying hierarchical data stored on a RDBMS using JPA:
https://jivimberg.io/blog/2018/08/04/recursive-queries-on-rdbms-with-jpa/
That explains how to create NamedEntityGraphs and use them.
Part of the code:
@Entity
@NamedEntityGraphs(
NamedEntityGraph(name = "womanWithDaughters",
attributeNodes = [NamedAttributeNode(value = "daughters", subgraph = "daughterWithDaughters")],
subgraphs = [
NamedSubgraph(
name = "daughterWithDaughters",
attributeNodes = [NamedAttributeNode("daughters")]
)
]
)
)
data class Woman (
fun findWomanUsingEntityGraph(id: Long): Woman {
val graph = em.createEntityGraph(Woman::class.java)
.also { it.addSubgraph<Woman>("daughters") }
return em.find(Woman::class.java, id, mapOf("javax.persistence.loadgraph" to graph))
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.