数据库多级联动表设计

设计社区数据库时,遇到有关

    物业公司 社区 楼栋 单元 房屋 的基础表设计。

    这几张表之间都是包含关系,故设计为此形式。有点类似于链表。为了叙述方便,暂且称大的概念为高级表,小的概念为低级表。低级表关系着一个高级表的主键id。以此可以查询到所有的数据。(此处为了表述简便,删除了其他无关字段)。设计如下:

此设计完全解开了各层之间的耦合性,用类似于链表的形式将多层数据关系起来。可以节约大量的数据库空间。而且如果发生了新增和修改操作时,花费的时间少,因为只要修改与之相关的那个表的主键即可。例如现在有数据如下。

deptcommunitybulidingunithouse
id=0id = 1
id=22id=33id=44
 dept_id = 0community_id=1buliding_id=22unit_id=33

如果此时需要修改buliding的id为88时,只需要修改unit的bulidingId,虽然house和buliding间接相关,但是不需要修改。修改后如下:

deptcommunitybulidingunithouse
id=0id = 1id=88id=33id=44
 dept_id = 0community_id=1buliding_id=88unit_id=33

此优势有点类似于java中的LinkedList与比较于ArrayList的优势。

此设计有点如下:

1:节约了数据库空间,在数据库空间有限的情况下,采用此方案可获得空间的优势。

2:便于增加和更新操作,类似于链表的结构,可以最大程度的减少对数据的修改。

但空间的优化,导致的缺点毫无疑问的就是时间的牺牲。以空间换取时间。

试想:若已知若干个房屋,想知道这些房屋到底归属于何物业公司(部门),所要执行的查询时多么复杂。

1:根据houseid遍历unit表,查询出与之相关的unit

2:根据unit查询buliding表,查询出与之相关的buliding

3:根据buliding查询community表,查询出与之相关的community

4:根据community查询dept表,查询出与之相关的dept

由此过程,光是查询一条记录,就需要查询4张表,当多级关系的级数越来越多时,就会变得越复杂。例如房屋与用户产生关系。查询用户所关联的物业公司,就需要查询5张表,而且一个用户可能有n套房产,那么每查询一次一个用户所关联的物业公司时,就需要查询5张数据表n次。

下面是笔者为此付出的代码:有心人可以发现,因为是面向接口编程的,所以如果需要替换实现的话,只需要替换implement就可以啦,故此将接口也粘了上去。

package com.kingen.modules.sys.service;


import java.util.List;
import java.util.Map;
import java.util.Set;

import com.kingen.modules.sys.entity.SysDeptEntity;
import com.kingen.modules.sys.entity.SysUserEntity;
import com.kingen.modules.wy.entity.WyBuildingEntity;
import com.kingen.modules.wy.entity.WyCommunityEntity;
import com.kingen.modules.wy.entity.WyHouseEntity;
import com.kingen.modules.wy.entity.WyUnitEntity;


/**
 * 系统用户
 *
 * @author System
 * @email sunlightcs@gmail.com
 * @date 2016年9月18日 上午9:43:39
 */
public interface SysUserService
{

    /**
     * 查询用户的所有权限
     *
     * @param userId 用户ID
     */
    List<String> queryAllPerms(Long userId);

    /**
     * 查询用户的所有菜单ID
     */
    List<Long> queryAllMenuId(Long userId);

    /**
     * 根据用户名,查询系统用户
     */
    SysUserEntity queryByUserName(String username);

    /**
     * 根据用户ID,查询用户
     *
     * @param userId
     * @return
     */
    SysUserEntity queryObject(Long userId);

    /**
     * 查询用户列表
     */
    List<SysUserEntity> queryList(Map<String, Object> map);

    /**
     * 查询总数
     */
    int queryTotal(Map<String, Object> map);

    /**
     * 保存用户
     */
    void save(SysUserEntity user);

    /**
     * 修改用户
     */
    void update(SysUserEntity user);

    /**
     * 删除用户
     */
    void deleteBatch(Long[] userIds);

    /**
     * 修改密码
     *
     * @param userId      用户ID
     * @param password    原密码
     * @param newPassword 新密码
     */
    int updatePassword(Long userId, String password, String newPassword);

    /**
     * 查询到此用户有关的所有房屋实体
     * @param userId 用户id
     * @return
     */
    Set<WyHouseEntity> queryHaveHouses(Long userId);

    /**
     * 查询到此用户有关的所有单元实体
     * @param userId
     * @return
     */
    Set<WyUnitEntity> queryHaveUnit(Long userId);

    /**
     * 查询到此用户有关的所有楼栋实体
     * @param userId
     * @return
     */
    Set<WyBuildingEntity> queryHaveBuilding(Long userId);

    /**
     * 查询到此用户有关的所有社区实体
     * @param userId
     * @return
     */
    Set<WyCommunityEntity> queryHaveCommunity(Long userId);

    /**
     * 注意!此方法查询到的是此用户的所拥有的部门id,即物业公司的id。
     * 即此人的信息被那些物业公司所持有。
     * 并不是指此人所属的部门,用户所属的部门即系统级别的部门,不归属与任何一个小区。
     * @param userId
     * @return
     */
    Set<SysDeptEntity> queryHaveDept(Long userId);
}
package com.kingen.modules.sys.service.impl;


import com.kingen.modules.sys.entity.SysDeptEntity;
import com.kingen.modules.sys.service.SysDeptService;
import com.kingen.modules.wy.entity.*;
import com.kingen.modules.wy.service.*;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.kingen.common.annotation.DataFilter;
import com.kingen.modules.sys.dao.SysUserDao;
import com.kingen.modules.sys.entity.SysUserEntity;
import com.kingen.modules.sys.service.SysUserRoleService;
import com.kingen.modules.sys.service.SysUserService;
import com.kingen.modules.sys.shiro.ShiroUtils;

import java.util.*;


/**
 * 系统用户
 *
 * @author System
 * @email sunlightcs@gmail.com
 * @date 2016年9月18日 上午9:46:09
 */
@Service("sysUserService" )
public class SysUserServiceImpl implements SysUserService
{
    @Autowired
    private SysUserDao sysUserDao;
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private WyHouseService wyHouseService;
    @Autowired
    private WyHouseUserService wyHouseUserService;
    @Autowired
    private WyUnitService wyUnitService;
    @Autowired
    private WyBuildingService wyBuildingService;
    @Autowired
    private WyCommunityService wyCommunityService;
    @Autowired
    private SysDeptService sysDeptService;
    @Override
    public List<String> queryAllPerms(Long userId)
    {
        return sysUserDao.queryAllPerms(userId);
    }

    @Override
    public List<Long> queryAllMenuId(Long userId)
    {
        return sysUserDao.queryAllMenuId(userId);
    }

    @Override
    public SysUserEntity queryByUserName(String username)
    {
        return sysUserDao.queryByUserName(username);
    }

    @Override
    public SysUserEntity queryObject(Long userId)
    {
        return sysUserDao.queryObject(userId);
    }

    @Override
    @DataFilter(tableAlias = "u", user = false)
    public List<SysUserEntity> queryList(Map<String, Object> map)
    {
        return sysUserDao.queryList(map);
    }

    @Override
    @DataFilter(tableAlias = "u", user = false)
    public int queryTotal(Map<String, Object> map)
    {
        return sysUserDao.queryTotal(map);
    }

    @Override
    @Transactional
    public void save(SysUserEntity user)
    {
        user.setCreateTime(new Date());
        //sha256加密
        String salt = RandomStringUtils.randomAlphanumeric(20);
        user.setSalt(salt);
        user.setPassword(ShiroUtils.sha256(user.getPassword(), user.getSalt()));
        sysUserDao.save(user);

        //保存用户与角色关系
        sysUserRoleService.saveOrUpdate(user.getUserId(), user.getRoleIdList());
    }

    @Override
    @Transactional
    public void update(SysUserEntity user)
    {
        if (StringUtils.isBlank(user.getPassword()))
        {
            user.setPassword(null);
        } else
        {
            user.setPassword(ShiroUtils.sha256(user.getPassword(), user.getSalt()));
        }
        sysUserDao.update(user);

        //保存用户与角色关系
        sysUserRoleService.saveOrUpdate(user.getUserId(), user.getRoleIdList());
    }

    @Override
    @Transactional
    public void deleteBatch(Long[] userId)
    {
        sysUserDao.deleteBatch(userId);
    }

    @Override
    public int updatePassword(Long userId, String password, String newPassword)
    {
        Map<String, Object> map = new HashMap<>();
        map.put("userId", userId);
        map.put("password", password);
        map.put("newPassword", newPassword);
        return sysUserDao.updatePassword(map);
    }

    @Override
    public Set<WyHouseEntity> queryHaveHouses(Long userId)
    {
        Set<WyHouseEntity> houses = new LinkedHashSet<>();
        Set<Long> houseIds = new HashSet<>();

        //先筛选一次房屋的id,使用Set确保无重复,减少查询操作,以及查询到的对象不一致的问题
        for (WyHouseUserEntity wyHouseUserEntity: wyHouseUserService.queryListByUserId(userId))
            houseIds.add(wyHouseUserEntity.getHouseId());

        //执行查询房屋,塞入结果中
        for (Long houseId : houseIds)
            houses.add(wyHouseService.queryObject(houseId));
        return houses;
    }

    @Override
    public Set<WyUnitEntity> queryHaveUnit(Long userId)
    {
        Set<WyUnitEntity> units = new LinkedHashSet<>();
        Set<Long> unitIds = new HashSet<>();

        Set<WyHouseEntity> houses = queryHaveHouses(userId);
        for(WyHouseEntity house : houses)
            unitIds.add(house.getUnitId());

        for (Long unitId : unitIds)
            units.add(wyUnitService.queryObject(unitId));

        return units;
    }

    @Override
    public Set<WyBuildingEntity> queryHaveBuilding(Long userId)
    {
        Set<WyBuildingEntity> buildings = new LinkedHashSet<>();
        Set<Long> buildingIds = new HashSet<>();

        Set<WyUnitEntity> units = queryHaveUnit(userId);
        for(WyUnitEntity unit : units)
            buildingIds.add(unit.getBulidingId());

        for (Long buildingId : buildingIds)
            buildings.add(wyBuildingService.queryObject(buildingId));

        return buildings;
    }

    @Override
    public Set<WyCommunityEntity> queryHaveCommunity(Long userId)
    {
        Set<WyCommunityEntity> communitys = new LinkedHashSet<>();
        Set<Long> communityIds = new HashSet<>();

        Set<WyBuildingEntity> buildings = queryHaveBuilding(userId);
        for(WyBuildingEntity building : buildings)
            communityIds.add(building.getCommunityId());

        for (Long communityId : communityIds)
            communitys.add(wyCommunityService.queryObject(communityId));

        return communitys;
    }

    @Override
    public Set<SysDeptEntity> queryHaveDept(Long userId)
    {
        Set<SysDeptEntity> depts = new LinkedHashSet<>();
        Set<Long> deptIds = new HashSet<>();

        Set<WyCommunityEntity> communitys = queryHaveCommunity(userId);
        for(WyCommunityEntity community : communitys)
            deptIds.add(community.getDeptId());

        for (Long deptId : deptIds)
            depts.add(sysDeptService.queryObject(deptId));

        return depts;
    }

}

由于严重的查询效率问题,以及成本问题考虑(毕竟数据库容量的增加,只需要增加硬盘,而服务器的性能增加,则需要cpu的投入,两者成本高低,不言而喻)。再者,基于用户友好的对待。也不应该牺牲用户的查询体验,使数据库的空间优化。

改进后的数据库设计如下。

以每一个低级持有到所有高级表的主键,从而获得一个查询性能上的提升。


此处还有一个缺陷,都为设计外键约束,需要时添加。

此设计在级数,也就是关联表数极多的情况下会多出极多的字段出来。可以使用此种设计时与第一种设计交互使用。但此操作会导致表的结构混乱,而实际操作中也很少出现级数很大的情况。

注意:

1:此设计会导致数据库有冗余字段,但是优化查询。

2:切记不可添加除外键之外的其他字段。例如不可在house这张表中添加community_name。只可添加community_id。不符合三范式。房屋可以有所在的社区,但不可有所在社区的名字,这是社区表的字段,请联表查询。

3:新增和修改操作,都要保持多表同步,请设立事务管理。导致新增和修改操作时间长。

此时的查询显而易见的得到了巨大的提升。


此两种设计方案,对比如下;

   优点缺点
前者

1:数据库所占空间小,空间最大优化。

2:新增和修改速度快,影响小。

1:查询速度慢

后者1:查询速度快

1:数据库的字段增多,空间牺牲

2:新增和删除操作复杂,操作缓慢。

具体使用哪一种表的设计方案,请结合项目情况选择。

文中有不当之处,还请各位多多指正!!


  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值