我有一个应用程序生成一个包含multiple layers of nested lists
的列表。我当前的实现会生成所需的结果,但它也会对数据库执行大量查询exponential to the number of layers depth
。所以整体表现相当差(花费太多时间)。我试图找到一种方法来改善它,但是,说实话,我不知道哪种方式更好。
我有所改善。我的改进可以接受吗? (不会创造其他问题)
还有其他方法可以改进,例如:
一个。非规范化数据库表
湾使用其他ORM框架和更好的支持
℃。数据库设计或java代码的其他更好的实现(可以真正使用你们的一些帮助)
我所拥有的是一些具有一对多关系的表格:
Family
id name
1 Smiths
2 Johnson
3 Williams
Member
id family_id name
1 1 David Smiths
2 1 Mary Smiths
3 1 William Smiths
3 2 David Johnson
4 3 David Williams
Asset
id member_id type value
1 1 cash 100.00
2 1 share 200.00
3 1 security 100.00
4 2 cash 50.00
我需要的是生成a list of Families and their Asset
。这应该是Family
的列表,每个Family
包含Member
的列表,每个Member
包含Asset
的列表:
class Family{
Integer id;
String name;
List<Member> members;
// Getter and setters
}
class Member{
Integer id;
Integer family_id;
String name;
List<Asset> assets;
// Getter and setters
}
class Asset{
Integer id;
Integer member_id;
String type;
BigDecimal value;
}
结果应为List<Family> families
。输出到JSON:
{
"families": [{
"id": 1,
"name": "Smiths",
"members": [{
"id": 1,
"family_id": 1,
"name": "David Smiths",
"assets": [{
"id": 1,
"member_id": 1,
"type": "cash",
"value": "100.00"
}, {
"id": 2,
"member_id": 1,
"type": "share",
"value": "200.00"
}, {
"id": 3,
"member_id": 1,
"type": "security",
"value": "100.00"
}]
}]
}]
}
到目前为止,我有两种方法可以做到这一点:
Java代码
List<Family> generateFamilyList(){
List<Family> families = resultMapper.selectFamily();
// select Member for each Family
for(Family family: families){
List<Member> membersInFamily = resultMapper.selectMemberByFamily(family.getId());
//select Asset for each Member
for(Member member: membersInFamily){
List<Asset> assetsOfMember = resultMapper.selectAssetByMember(member.getId());
member.setAssets(assetsOfMember);
}
family.setMembers(membersInFamily );
}
return families;
}
和声明,使用Mybatis。
<select id="selectFamily" resultType="Family">
select id, name from family;
</select>
<select id="selectMemberByFamily" resultType="Member">
select id, family_id, name from member
where family_id = #{familyId};
</select>
<select id="selectAssetByMember" resultType="Asset">
select id, member_id, type, value from asset
where member_id = #{memberId};
</select>
此方法完成其工作并生成正确的JSON。但它也会在最里面选择N^3 times of query to the database
selectAssetByMember(member.getId())
我切换到mapper中使用Mybatis collection select,但发现只有make Mybatis Mapper
执行for循环查询而不是在java代码中执行。数据库仍然接收N ^ 3查询。
所以我提出了另一个解决方案:
Java代码
List<Family> generateFamilyList(){
List<Family> families = resultMapper.selectFamily();
List<Integer> allFamilyIds = familes.stream()
.map(Family::getId)
.collect(Collectors.toList());
if(familyIds.isEmpty())
//throw exception or return
// select all members in one query.
// !Watch out! for Max Number of Allowable Parameters
List<Member> allMembers = resultMapper.selectMemberByFamilyIds(allFamilyIds);
List<Integer> allMemberIds = allMembers.stream()
.map(Member::getId)
.collect(Collectors.toList());
if(allMemberIds.isEmpty())
//throw exception or
//return a new empty ArrayList for this member's asset
// select all ssets in one query.
// !Watch out! for Max Number of Allowable Parameters
List<Asset> allAssets = resultMapper.selectAssetByMemberIds(allMemberIds );
// filter and set Members for each Family
for(Family family: families){
List<Member> membersInFamily = allMembers.stream()
.filter(member -> member.getFamilyId().equals(family.getId()))
.collect(Collectors.toList());
//select Asset for each Member
for(Member member: membersInFamily){
List<Asset> assetsOfMember = allAssets.stream()
.filter(asset -> asset.getMemberId().equals(member.getId()))
.collect(Collectors.toList());
member.setAssets(assetsOfMember);
}
family.setMembers(membersInFamily );
}
return families;
}
和陈述。 mybatis支持一系列参数:
<select id="selectFamily" resultType="Family">
select id, name from family;
</select>
<select id="selectMemberByFamilyIds" resultType="Member">
select id, family_id, name from member
where family_id IN
<foreach> #{familyId} </foreach>;
<!-- ( 1, 2, 3, ....); the input list familyIds-->
<!-- could cause SQLException if the ids more than the Max Number of Allowable Parameters of database -->
</select>
<select id="selectAssetByMemberIds" resultType="Asset">
select id, member_id, type, value from asset
where member_id IN
<foreach> #{memberId} </foreach>
<!-- ( 1, 2, 3, ....); the input list memberIds-->
<!-- could cause SQLException if the ids more than the Max Number of Allowable Parameters of database -->
</select>
查询号码减少
Java应用程序中的内存使用量增加
整体应用程序响应时间显着缩短(在我的情况下降至20% - 10%)
新问题:Max Number of Allowable Parameters。如果参数列表memberIds
的大小太大,则需要在查询之前进行分页。最大数量有限,与数据库类型不同。
这就是我现在所得到的一切。而且我有点卡住,不知道我应该在哪里改进我的代码。
感谢各位观看我的长期问题。
答案 0 :(得分:2)
这实际上取决于我们谈论的数据量,保存的数据等等。
对于真正的大数据解决方案,我们很难说像cassandra中的图形数据库,通过akka连接。
对于中等负载,数据库将位于自己的服务器上,因此您需要减少连接数量和网络使用量,因此最好在本地进行一次调用和排序,
对于非常小的数据,其中app和db位于同一服务器上,对db的多次调用是可以的,并且db在排序时会更好更快。
答案 1 :(得分:1)
如果您真的想要最佳性能,那么您的过滤应该在数据所在的位置附近。这意味着使用您的数据库功能最大化。例如,MySQL支持JSON_OBJECT
来创建 JSON 结果。
例如:
SELECT
member.id,
member.name,
member.family_id,
JSON_OBJECT('id', asset.id,'member_id', asset.member_id, 'type', asset.type, 'value',
asset.value) AS assets
FROM
member
LEFT OUTER JOIN
asset
ON
member.id = asset.member_id
会给你以下结果。 你可以进一步微调。
1 David Smiths 1 {"id": 1, "type": "cash", "value": 100, "member_id": 1}
1 David Smiths 1 {"id": 2, "type": "share", "value": 200, "member_id": 1}
1 David Smiths 1 {"id": 3, "type": "security", "value": 100, "member_id": 1}
2 Mary Smiths 1 {"id": 4, "type": "cash", "value": 50, "member_id": 2}
3 William Smiths 1 {"id": null, "type": null, "value": null, "member_id": null}
4 David Johnson 2 {"id": null, "type": null, "value": null, "member_id": null}
5 David Williams 3 {"id": null, "type": null, "value": null, "member_id": null}
希望这有帮助。