最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用AOP+反射實現(xiàn)自定義Mybatis多表關(guān)聯(lián)查詢

 更新時間:2022年05月25日 15:12:51   作者:sushengbuyu  
這篇文章主要介紹了使用AOP+反射實現(xiàn)自定義Mybatis多表關(guān)聯(lián),目前的需求是增強(qiáng)現(xiàn)有的查詢,使用簡單的注解即可實現(xiàn)多表關(guān)聯(lián),本文通過實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下

一、需求

目前使用的ORM框架是Mybatis Plus,是Mybatis的增強(qiáng)框架,基礎(chǔ)的CRUD的方法都集成了,開發(fā)起來很是方便。但是項目中總是需要多表關(guān)聯(lián)查詢。

Mybatis的多表關(guān)聯(lián)有兩種

一、在Mapper中使用@Result @One @Many注解

二、在xml文件中配置對應(yīng)的resultMap和關(guān)聯(lián)標(biāo)簽

使用起來很不方便。JPA倒是有多表關(guān)聯(lián)的注解實現(xiàn),但是不想再引入另一個ORM框架。

目前的需求是增強(qiáng)現(xiàn)有的查詢,使用簡單的注解即可實現(xiàn)多表關(guān)聯(lián)。

二、核心代碼

GitHub:https://github.com/sushengbuyu/mybatis-mapping-demo

實現(xiàn)該功能總共需要四個文件

兩個自定義注解,一個虛擬Mapper,一個切面處理類

源碼

MapTo

自定義映射注解,標(biāo)注需要映射處理的字段

import java.lang.annotation.*;
/**
 * @author victor
 * @desc 自定義多表關(guān)聯(lián)映射注解
 * @date 2022/5/23
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MapTo {
    /**
     * 映射目標(biāo)
     */
    Class<?> targetClass();

    /**
     * 執(zhí)行SQL
     */
    String sql();

    /**
     * 嵌套處理
     * 為true時,如果映射的對象類中有映射字段,也執(zhí)行映射操作
     */
    boolean doDeep() default false;
}

DoMap

自定義映射處理注解,標(biāo)注需要執(zhí)行映射的方法

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @author victor
 * @desc 標(biāo)注該需要執(zhí)行映射處理的方法
 * @date 2022/5/23
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DoMap {
    /**
     * 需要處理映射的類
     * @return Class
     */
    Class<?> targetClass();

    /**
     * spel表達(dá)式
     * 默認(rèn)為空
     * @return String
     */
    String spel() default "";
}

IDualMapper

虛擬Mapper,用來執(zhí)行自定義SQL

import java.util.List;
import java.util.Map;
/**
 * @author victor
 * @desc 虛擬Mapper
 * @date 2022/5/23
 */
@Mapper
public interface IDualMapper {

    /**
     * 執(zhí)行自定義SQL
     * @param sql sql
     * @return List<Map<String, Object>>
     */
    List<Map<String, Object>> executeSql(String sql);
}

DualMapper

使用者自行實現(xiàn)DualMapper,解除mybatis強(qiáng)依賴

package sushengbuyu.maptodemo.sys.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import sushengbuyu.maptodemo.aop.IDualMapper;
import java.util.List;
import java.util.Map;
/**
 * @author victor
 * @desc 虛擬Mapper
 * @date 2022/5/23
 */
@Mapper
public interface DualMapper extends IDualMapper {
    /**
     * 執(zhí)行自定義SQL
     * @param sql sql
     * @return List<Map<String, Object>>
     */
    @Select("${sql}")
    List<Map<String, Object>> executeSql(@Param("sql") String sql);
}

DoMapAspect

切面處理類,核心代碼,執(zhí)行映射操作

package sushengbuyu.maptodemo.aop;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @author victor
 * @desc 自定義關(guān)聯(lián)映射切面
 * @date 2022/5/23
 */
@Component
@Aspect
public class DoMapAspect {
    private final static Logger log = LoggerFactory.getLogger(DoMapAspect.class);
    /**
     * 保存MapTo映射關(guān)系
     * key 映射字段所在類
     * value 映射字段集合
     */
    private static final Map<Class<?>, Set<String>> MAPPING = new HashMap<>(8);
    private final IDualMapper dualMapper;
    public DoMapAspect(DualMapper dualMapper) {
        this.dualMapper = dualMapper;
    }
    /**
     * 初始化映射關(guān)系
     * 掃描指定包下所有類,找出帶有MapTo注解的字段
     * 存儲映射數(shù)據(jù)
     */
    @PostConstruct
    public void initMap() {
        // 初始化所有MapTo對象
        // 掃描所有類
        Set<Class<?>> classes = ClassUtil.scanPackage("sushengbuyu.maptodemo");
        int totalField = 0;
        // 找出使用MapTo注解的對象
        for (Class<?> c : classes) {
            Field[] fields = c.getDeclaredFields();
            for (Field f : fields) {
                if (null != f.getAnnotation(MapTo.class)){
                    log.info("找到需要映射的字段: 類名:{} - 字段名:{}", c.getName(), f.getName());
                    // 保存映射關(guān)系
                    Set<String> set;
                    if (MAPPING.containsKey(c)) {
                        set = MAPPING.get(c);
                    } else {
                        set = new HashSet<>();
                    }
                    set.add(f.getName());
                    MAPPING.put(c, set);
                    totalField++;
                }
            }
        }
        log.info("總計{}個映射類,{}個映射字段", MAPPING.size(), totalField);
    }
    /**
     * 切點
     * @param doMap 執(zhí)行映射注解
     */
    @Pointcut("@annotation(doMap)")
    public void point(DoMap doMap){}
    /**
     * 處理關(guān)聯(lián)映射
     * @param point 切點
     * @param doMap 映射處理配置
     * @return Object
     * @throws Throwable 異常
     */
    @Around(value = "@annotation(doMap)")
    public Object doMap(ProceedingJoinPoint point, DoMap doMap) throws Throwable {
        // 執(zhí)行切面方法
        Object obj = point.proceed();
        try {
            Object relObj = obj;
            if (StringUtils.hasLength(doMap.spel())) {
                // 如果使用了SPEL表達(dá)式,則從返回值中獲取處理對象
                ExpressionParser parser = new SpelExpressionParser();
                Expression expression = parser.parseExpression(doMap.spel());
                relObj = expression.getValue(obj);
            }
            // 獲取映射類
            Class<?> c = doMap.targetClass();
            // 映射處理
            doMapping(c, relObj);
        } catch (Exception e) {
            log.error("映射異常", e);
        }
        log.info("返回對象:{}", obj);
        return obj;
    }
    private void doMapping(Class<?> c, Object obj) throws Exception {
        if (obj instanceof Collection) {
            // 集合
            Collection<?> co = (Collection<?>) obj;
            for (Object o : co) {
                mapping(c, o);
            }
        } else {
            // 單個對象
            mapping(c, obj);
        }
    }
    private void mapping(Class<?> c, Object obj) throws Exception {
        // 判斷是否有映射關(guān)系
        if (MAPPING.containsKey(c)) {
            log.info("處理映射類:{}", c.getName());
            // 從緩存中獲取映射字段名稱
            Set<String> filedNames = MAPPING.get(c);
            for (String fieldName : filedNames) {
                Field f = c.getDeclaredField(fieldName);
                log.info("處理映射字段:{}", f.getName());
                // 獲取映射注解
                MapTo mapTo = f.getAnnotation(MapTo.class);
                log.info("映射配置:{}", mapTo);
                // 設(shè)置私有字段訪問權(quán)限
                f.setAccessible(true);
                // 執(zhí)行SQL
                String sql = mapTo.sql();
                // 處理SQL變量
                List<String> res = ReUtil.findAll("\$\{(.*?)}", sql, 0);
                log.info("SQL變量:{}", res);
                for (String re : res) {
                    Field ff = obj.getClass().getDeclaredField(re.substring(2, re.length()-1));
                    ff.setAccessible(true);
                    Object o = ff.get(obj);
                    sql = sql.replace(re, o.toString());
                }
                log.info("最終SQL:{}", sql);
                List<Map<String, Object>> results = dualMapper.executeSql(sql);
                Object v = null;
                if (Collection.class.isAssignableFrom(f.getType())) {
                    // 集合對象
                    if (results.size() > 0) {
                        v = results.stream()
                                .map(r -> mapToBean(r, mapTo.targetClass()))
                                .collect(Collectors.toList());
                    }
                } else {
                    // 單個對象
                    if (results.size() > 1) {
                        log.error("預(yù)計返回一條數(shù)據(jù),實際返回多條數(shù)據(jù)。執(zhí)行SQL: {}", sql);
                    } else if (results.size() == 1) {
                        // 轉(zhuǎn)換結(jié)果,賦值
                        v = mapToBean(results.get(0), mapTo.targetClass());
                    }
                }
                if (v != null && mapTo.doDeep()) {
                    doMapping(mapTo.targetClass(), v);
                }
                f.set(obj, v);
            }
        }
    }
    /**
     * Map對象轉(zhuǎn)Bean
     * @param map map
     * @param clazz bean
     * @return bean
     */
    private Object mapToBean(Map<?, ?> map, Class<?> clazz) {
        try {
            return BeanUtil.fillBeanWithMap(map, clazz.newInstance(), true);
        } catch (InstantiationException | IllegalAccessException e) {
            log.error("實例化異常", e);
            return null;
        }
    }
}

三、使用方法

測試類

SysUser

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import java.util.List;
import java.util.StringJoiner;
/**
 * @author victor
 * @desc 系統(tǒng)用戶
 * @date 2022/5/17
 */
public class SysUser implements Serializable {
    private static final long serialVersionUID = 4855472141572371097L;
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    /**
     * 登錄用戶名
     */
    private String username;
    /**
     * 登錄密碼
     */
    private String password;
    /**
     * 昵稱
     */
    private String nickName;
    
    @MapTo(targetClass = SysRole.class
            , doDeep = true
            , sql = "SELECT * FROM sys_role WHERE user_id=${id}")
    @TableField(exist = false)
    private SysRole sysRole;
    @MapTo(targetClass = SysRole.class
            , doDeep = true
            , sql = "SELECT * FROM sys_role WHERE user_id=${id}")
    @TableField(exist = false)
    private List<SysRole> roleList;

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getNickName() {
        return nickName;
    }
    public void setNickName(String nickName) {
        this.nickName = nickName;
    }
    public SysRole getSysRole() {
        return sysRole;
    }
    public void setSysRole(SysRole sysRole) {
        this.sysRole = sysRole;
    }
    public List<SysRole> getRoleList() {
        return roleList;
    }
    public void setRoleList(List<SysRole> roleList) {
        this.roleList = roleList;
    }
    @Override
    public String toString() {
        return new StringJoiner(", ", SysUser.class.getSimpleName() + "[", "]")
                .add("id=" + id)
                .add("username='" + username + "'")
                .add("password='" + password + "'")
                .add("nickName='" + nickName + "'")
                .add("sysRole=" + sysRole)
                .add("roleList=" + roleList)
                .toString();
    }
}

SysRole

package sushengbuyu.maptodemo.sys.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import sushengbuyu.maptodemo.aop.MapTo;
import java.util.List;
import java.util.StringJoiner;
/**
 * @author victor
 * @desc 說明
 * @date 2022/5/23
 */
public class SysRole {
    @TableId
    private Long id;
    private Long userId;
    private String name;
    @MapTo(targetClass = SysPermission.class
            , sql = "SELECT p.* FROM sys_permission p " +
            "LEFT JOIN sys_role_permission rp ON p.id = rp.perm_id " +
            "WHERE rp.role_id = ${id}")
    @TableField(exist = false)
    private List<SysPermission> permissionList;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public Long getUserId() {
        return userId;
    }
    public void setUserId(Long userId) {
        this.userId = userId;
    }
    public List<SysPermission> getPermissionList() {
        return permissionList;
    }
    public void setPermissionList(List<SysPermission> permissionList) {
        this.permissionList = permissionList;
    }
    @Override
    public String toString() {
        return new StringJoiner(", ", SysRole.class.getSimpleName() + "[", "]")
                .add("id=" + id)
                .add("userId=" + userId)
                .add("name='" + name + "'")
                .toString();
    }
}

SysPermission

package sushengbuyu.maptodemo.sys.po;
import java.util.StringJoiner;
/**
 * @author victor
 * @desc 說明
 * @date 2022/5/25
 */
public class SysPermission {
    private Long id;
    private String name;
    private Integer type;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getType() {
        return type;
    }
    public void setType(Integer type) {
        this.type = type;
    }
    @Override
    public String toString() {
        return new StringJoiner(", ", SysPermission.class.getSimpleName() + "[", "]")
                .add("id=" + id)
                .add("name='" + name + "'")
                .add("type=" + type)
                .toString();
    }
}

SysUserService

測試用例就常見的三種, 查單個,查列表,查分頁

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.List;
/**
 * @author victor
 * @desc 說明
 * @date 2022/5/17
 */
@Service
public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> {
    @DoMap(targetClass = SysUser.class)
    @Override
    public SysUser getById(Serializable id) {
        return super.getById(id);
    }
    @DoMap(targetClass = SysUser.class)
    public List<SysUser> listAll() {
        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        return baseMapper.selectList(wrapper);
    }
    /**
     * 從Page中取records作為處理對象
     */
    @DoMap(targetClass = SysUser.class, spel = "records")
    public Page<SysUser> page() {
        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        Page<SysUser> p = new Page<>(1, 10);
        return baseMapper.selectPage(p, wrapper);
    }
}

DoMapTests

import cn.hutool.json.JSONUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
//@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DoMapTests {
    @Autowired
    private SysUserService service;
    @Test
    void single() {
        System.out.println(JSONUtil.toJsonPrettyStr(service.getById(1)));
    }
    @Test
    void list() {
        System.out.println(JSONUtil.toJsonPrettyStr(service.listAll()));
    }
    @Test
    void page() {
        System.out.println(JSONUtil.toJsonPrettyStr(service.page()));
    }
}

測試數(shù)據(jù)

測試結(jié)果

single

{
    "nickName": "aa11",
    "roleList": [
        {
            "permissionList": [
                {
                    "type": 0,
                    "name": "add",
                    "id": 1
                },
                {
                    "type": 0,
                    "name": "query",
                    "id": 2
                }
            ],
            "userId": 1,
            "name": "r1",
            "id": 11
        },
        {
            "permissionList": [
                {
                    "type": 0,
                    "name": "del",
                    "id": 3
                }
            ],
            "userId": 1,
            "name": "r2",
            "id": 12
        }
    ],
    "password": "123456",
    "id": 1,
    "username": "a1"
}

list

[
    {
        "nickName": "aa11",
        "roleList": [
            {
                "permissionList": [
                    {
                        "type": 0,
                        "name": "add",
                        "id": 1
                    },
                    {
                        "type": 0,
                        "name": "query",
                        "id": 2
                    }
                ],
                "userId": 1,
                "name": "r1",
                "id": 11
            },
            {
                "permissionList": [
                    {
                        "type": 0,
                        "name": "del",
                        "id": 3
                    }
                ],
                "userId": 1,
                "name": "r2",
                "id": 12
            }
        ],
        "password": "123456",
        "id": 1,
        "username": "a1"
    },
    {
        "sysRole": {
            "userId": 2,
            "name": "r3",
            "id": 13
        },
        "nickName": "aa22",
        "roleList": [
            {
                "userId": 2,
                "name": "r3",
                "id": 13
            }
        ],
        "password": "123456",
        "id": 2,
        "username": "a2"
    }
]

page

{
    "optimizeCountSql": true,
    "records": [
        {
            "nickName": "aa11",
            "roleList": [
                {
                    "permissionList": [
                        {
                            "type": 0,
                            "name": "add",
                            "id": 1
                        },
                        {
                            "type": 0,
                            "name": "query",
                            "id": 2
                        }
                    ],
                    "userId": 1,
                    "name": "r1",
                    "id": 11
                },
                {
                    "permissionList": [
                        {
                            "type": 0,
                            "name": "del",
                            "id": 3
                        }
                    ],
                    "userId": 1,
                    "name": "r2",
                    "id": 12
                }
            ],
            "password": "123456",
            "id": 1,
            "username": "a1"
        },
        {
            "sysRole": {
                "userId": 2,
                "name": "r3",
                "id": 13
            },
            "nickName": "aa22",
            "roleList": [
                {
                    "userId": 2,
                    "name": "r3",
                    "id": 13
                }
            ],
            "password": "123456",
            "id": 2,
            "username": "a2"
        }
    ],
    "searchCount": true,
    "total": 0,
    "current": 1,
    "size": 10,
    "orders": [
    ]
}

到此這篇關(guān)于使用AOP+反射實現(xiàn)自定義Mybatis多表關(guān)聯(lián)的文章就介紹到這了,更多相關(guān)Mybatis多表關(guān)聯(lián)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 學(xué)習(xí)Java HashMap,看這篇就夠了

    學(xué)習(xí)Java HashMap,看這篇就夠了

    這篇文章主要介紹了Java HashMap的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • Java實現(xiàn)瀏覽器大文件上傳的示例詳解

    Java實現(xiàn)瀏覽器大文件上傳的示例詳解

    文件上傳是許多項目都有的功能,用戶上傳小文件速度一般都很快,但如果是大文件幾個g,幾十個g的時候,上傳了半天,馬上就要完成的時候,網(wǎng)絡(luò)波動一下,文件又要重新上傳,所以本文給大家介紹了Java實現(xiàn)瀏覽器大文件上傳的示例,需要的朋友可以參考下
    2024-07-07
  • 使用Java增刪改查數(shù)據(jù)庫的操作方法

    使用Java增刪改查數(shù)據(jù)庫的操作方法

    這篇文章主要介紹了使用Java增刪改查數(shù)據(jù)庫的操作方法,本文通過實例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-12-12
  • SpringBoot實現(xiàn)本地存儲文件上傳及提供HTTP訪問服務(wù)的方法

    SpringBoot實現(xiàn)本地存儲文件上傳及提供HTTP訪問服務(wù)的方法

    這篇文章主要介紹了SpringBoot實現(xiàn)本地存儲文件上傳及提供HTTP訪問服務(wù),本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • 啟動 Eclipse 彈出 Failed to load the JNI shared library jvm.dll 錯誤的解決方法

    啟動 Eclipse 彈出 Failed to load the JNI shared library jvm.dll

    這篇文章主要介紹了有時候,新電腦上回碰到打開Eclipse時,彈出提示“Failed to load the JNI shared library jvm.dll”錯誤,這里給大家分享解決方案
    2016-08-08
  • Java?spring?boot實現(xiàn)批量刪除功能詳細(xì)示例

    Java?spring?boot實現(xiàn)批量刪除功能詳細(xì)示例

    這篇文章主要給大家介紹了關(guān)于Java?spring?boot實現(xiàn)批量刪除功能的相關(guān)資料,文中通過代碼以及圖文將實現(xiàn)的方法介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2023-08-08
  • Java隨機(jī)數(shù)的5種獲得方法(非常詳細(xì)!)

    Java隨機(jī)數(shù)的5種獲得方法(非常詳細(xì)!)

    這篇文章主要給大家介紹了關(guān)于Java隨機(jī)數(shù)的5種獲得方法,在實際開發(fā)中產(chǎn)生隨機(jī)數(shù)的使用是很普遍的,所以在程序中進(jìn)行產(chǎn)生隨機(jī)數(shù)操作很重要,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-10-10
  • 詳解RestTemplate?用法

    詳解RestTemplate?用法

    RestTemplate?是從?Spring3.0?開始支持的一個?HTTP?請求工具,也有的稱之為網(wǎng)絡(luò)框架,說白了就是Java版本的一個postman,這篇文章主要介紹了詳解RestTemplate?用法,需要的朋友可以參考下
    2022-07-07
  • MyBatis中使用foreach循環(huán)的坑及解決

    MyBatis中使用foreach循環(huán)的坑及解決

    這篇文章主要介紹了MyBatis中使用foreach循環(huán)的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Spring boot項目使用thymeleaf模板過程詳解

    Spring boot項目使用thymeleaf模板過程詳解

    這篇文章主要介紹了Spring boot項目使用thymeleaf模板過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-07-07

最新評論

密山市| 蓬莱市| 五河县| 肥东县| 永仁县| 长白| 四会市| 舟山市| 嘉义县| 文成县| 天津市| 崇州市| 全南县| 繁峙县| 盐源县| 建宁县| 阿坝| 库伦旗| 伊川县| 芦山县| 玛沁县| 甘南县| 沈阳市| 高邑县| 凤山市| 台湾省| 崇左市| 平昌县| 崇仁县| 卫辉市| 萨嘎县| 精河县| 阳江市| 大石桥市| 黄石市| 收藏| 稻城县| 铅山县| 新宾| 天峨县| 独山县|