Mybatis-Plus使用全解


前言

之前寫了《SpringBoot | 第九章:Mybatis-plus的集成和使用》一文,只是簡單的使用條件構造器列舉了一些通用的CURD操作。本人也想寫一篇通用的關於mybatis-plus的使用示例,一方面也讓自己更加了解mybatismybatis-plus,另一方面,也因為很多新人剛入職公司時,對這塊不是很熟悉,會有一些疑惑。所以,總的來說還是作為一份資產,可供人查閱,這樣也能減少了很多的溝通成本。


所以本章節,就主要來講解下關於Mybatis-plus的不同場景的用法,目前主要想到的是以下幾個知識點,也是很常用的知識點了,后面有補充的會再啟章節來記錄的。另外,官網的文檔已經很詳盡了,大家可認真查閱下。

  1. 代碼生成器
  2. 通用的CURD
  3. 條件構造器
  4. 自定義SQL語句
  5. 分頁插件、性能分析插件
  6. 公共字段自動填充

工程准備

這里還是以user表為例子,數據庫為mysql

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(20) DEFAULT NULL COMMENT '唯一標示',
  `code` varchar(20) DEFAULT NULL COMMENT '編碼',
  `name` varchar(64) DEFAULT NULL COMMENT '名稱',
  `status` char(1) DEFAULT '1' COMMENT '狀態 1啟用 0 停用',
  `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
  `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改時間'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

關於SpringBootMybatis-plus的集成,這里就不闡述了,這個不是今天的重點。不熟悉的同學,可移步:http://blog.lqdev.cn/2018/07/21/springboot/chapter-nine/進行查看了解。

代碼生成器

Mybatis-Plus已經提供了大量的自定義設置,生成的代碼完全能夠滿足各類型的需求,基本覆蓋了大部分的配置了。這里貼一個比較完整的代碼生成器類,大家可根據實際情況進行修改。

MysqlGenerator.java:

//省略了import
public class MysqlGenerator {

    /**
     * 包名
     */
    private static final String PACKAGE_NAME = "cn.lqdev.learning.mybatisplus.samples";
    /**
     * 模塊名稱
     */
    private static final String MODULE_NAME = "biz";
    /**
     * 輸出文件的路徑
     */
    private static final String OUT_PATH = "D:\\develop\\code";
    /**
     * 代碼生成者
     */
    private static final String AUTHOR = "oKong";

    /**
     * JDBC相關配置
     */
    private static final String DRIVER = "com.mysql.jdbc.Driver";
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/learning?useUnicode=true&characterEncoding=UTF-8";
    private static final String USER_NAME = "root";
    private static final String PASSWORD = "bs";

    /**
     * <p>
     * MySQL 生成演示
     * </p>
     */
    public static void main(String[] args) {
        // 自定義需要填充的字段
        List<TableFill> tableFillList = new ArrayList<TableFill>();
        //如 每張表都有一個創建時間、修改時間
        //而且這基本上就是通用的了,新增時,創建時間和修改時間同時修改
        //修改時,修改時間會修改,
        //雖然像Mysql數據庫有自動更新幾只,但像ORACLE的數據庫就沒有了,
        //使用公共字段填充功能,就可以實現,自動按場景更新了。
        //如下是配置
        TableFill createField = new TableFill("gmt_create", FieldFill.INSERT); 
        TableFill modifiedField = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE); 
        tableFillList.add(createField);
        tableFillList.add(modifiedField);
        
        // 代碼生成器
        AutoGenerator mpg = new AutoGenerator().setGlobalConfig(
                // 全局配置
                new GlobalConfig().setOutputDir(OUT_PATH)// 輸出目錄
                        .setFileOverride(true)// 是否覆蓋文件
                        .setActiveRecord(true)// 開啟 activeRecord 模式
                        .setEnableCache(false)// XML 二級緩存
                        .setBaseResultMap(false)// XML ResultMap
                        .setBaseColumnList(true)// XML columList
                        .setAuthor(AUTHOR)
                        // 自定義文件命名,注意 %s 會自動填充表實體屬性!
                        .setXmlName("%sMapper").setMapperName("%sDao")
        // .setServiceName("MP%sService")
        // .setServiceImplName("%sServiceDiy")
        // .setControllerName("%sAction")
        ).setDataSource(
                // 數據源配置
                new DataSourceConfig().setDbType(DbType.MYSQL)// 數據庫類型
                        .setTypeConvert(new MySqlTypeConvert() {
                            // 自定義數據庫表字段類型轉換【可選】
                            @Override
                            public DbColumnType processTypeConvert(String fieldType) {
                                System.out.println("轉換類型:" + fieldType);
                                // if ( fieldType.toLowerCase().contains( "tinyint" ) ) {
                                // return DbColumnType.BOOLEAN;
                                // }
                                return super.processTypeConvert(fieldType);
                            }
                        }).setDriverName(DRIVER).setUsername(USER_NAME).setPassword(PASSWORD).setUrl(URL))
                .setStrategy(
                        // 策略配置
                        new StrategyConfig()
                                // .setCapitalMode(true)// 全局大寫命名
                                .setDbColumnUnderline(true)// 全局下划線命名
                                // .setTablePrefix(new String[]{"unionpay_"})// 此處可以修改為您的表前綴
                                .setNaming(NamingStrategy.underline_to_camel)// 表名生成策略
                                // .setInclude(new String[] {"user"}) // 需要生成的表
                                // .setExclude(new String[]{"test"}) // 排除生成的表
                                // 自定義實體,公共字段
                                // .setSuperEntityColumns(new String[]{"test_id"})
                                .setTableFillList(tableFillList)
                                // 自定義實體父類
                                // .setSuperEntityClass("com.baomidou.demo.base.BsBaseEntity")
                                // // 自定義 mapper 父類
                                // .setSuperMapperClass("com.baomidou.demo.base.BsBaseMapper")
                                // // 自定義 service 父類
                                // .setSuperServiceClass("com.baomidou.demo.base.BsBaseService")
                                // // 自定義 service 實現類父類
                                // .setSuperServiceImplClass("com.baomidou.demo.base.BsBaseServiceImpl")
                                // 自定義 controller 父類
                                // .setSuperControllerClass("com.baomidou.demo.TestController")
                                // 【實體】是否生成字段常量(默認 false)
                                // public static final String ID = "test_id";
                                .setEntityColumnConstant(true)
                                // 【實體】是否為構建者模型(默認 false)
                                // public User setName(String name) {this.name = name; return this;}
                                .setEntityBuilderModel(true)
                                // 【實體】是否為lombok模型(默認 false)<a href="https://projectlombok.org/">document</a>
                                .setEntityLombokModel(true)
                // Boolean類型字段是否移除is前綴處理
                // .setEntityBooleanColumnRemoveIsPrefix(true)
                // .setRestControllerStyle(true)
                // .setControllerMappingHyphenStyle(true)
                ).setPackageInfo(
                        // 包配置
                        new PackageConfig().setModuleName(MODULE_NAME).setParent(PACKAGE_NAME)// 自定義包路徑
                                .setController("controller")// 這里是控制器包名,默認 web
                                .setXml("mapper").setMapper("dao")

                ).setCfg(
                        // 注入自定義配置,可以在 VM 中使用 cfg.abc 設置的值
                        new InjectionConfig() {
                            @Override
                            public void initMap() {
                                Map<String, Object> map = new HashMap<String, Object>();
                                map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
                                this.setMap(map);
                            }
                        }.setFileOutConfigList(
                                Collections.<FileOutConfig>singletonList(new FileOutConfig("/templates/mapper.xml.vm") {
                                    // 自定義輸出文件目錄
                                    @Override
                                    public String outputFile(TableInfo tableInfo) {
                                        return OUT_PATH + "/xml/" + tableInfo.getEntityName() + "Mapper.xml";
                                    }
                                })))
                .setTemplate(
                        // 關閉默認 xml 生成,調整生成 至 根目錄
                        new TemplateConfig().setXml(null)
        // 自定義模板配置,模板可以參考源碼 /mybatis-plus/src/main/resources/template 使用 copy
        // 至您項目 src/main/resources/template 目錄下,模板名稱也可自定義如下配置:
        // .setController("...");
        // .setEntity("...");
        // .setMapper("...");
        // .setXml("...");
        // .setService("...");
        // .setServiceImpl("...");
        );

        // 執行生成
        mpg.execute();
    }

}

 

按以上代碼生成器,生成的目錄結構如下(依賴中需要加入velocity-engine-core包,是利用模版引擎來生成的)

工程結構

對應mapper.xml

對於需要自定義模版時,大家可查看官方的mybatis-plus-generate包,默認的官方模版都放在此包下。

官方模版

有了代碼生成器,省了很多機械性的復制黏貼操作,還不會出錯,寫錯了再執行一次就好了!

通用的CURD

MP提供了ActiveRecord的支持,所以實體類只需繼承 Model 類即可實現基本 CRUD 操作。

這里以編寫測試類的形式,進行通用CURD操作,代碼類有相應的注釋說明。

GeneralTest.java:

/**
 * 通用CURD示例
 * @author oKong
 *
 */
@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用於測試的注解,可指定啟動類或者測試環境等,這里直接默認。
@SpringBootTest 
@Slf4j
public class GeneralTest {

    @Autowired
    IUserService userService;
    
    @Test
    public void testInsert() {
        User user = new User();
        user.setCode("001");
        user.setName("okong-insert");
        //默認的插入策略為:FieldStrategy.NOT_NULL,即:判斷 null
        //對應在mapper.xml時寫法為:<if test="field!=null">
        //這個可以修改的,設置字段的@TableField(strategy=FieldStrategy.NOT_EMPTY)
        //所以這個時候,為null的字段是不會更新的,也可以開啟性能插件,查看sql語句就可以知道
        userService.insert(user);
        
        //新增所有字段,
        userService.insertAllColumn(user);
        log.info("新增結束");
    }
    
    @Test
    public void testUpdate() {
        
        User user = new User();
        user.setCode("101");
        user.setName("oKong-insert");
        //這就是ActiveRecord的功能
        user.insert();
        //也可以直接 userService.insert(user);

        //更新
        User updUser = new User();
        updUser.setId(user.getId());
        updUser.setName("okong-upd");
        
        updUser.updateById();
        log.info("更新結束");
    }
    
    @Test
    public void testDelete() {
        User user = new User();
        user.setCode("101");
        user.setName("oKong-delete");
        
        user.insert();
        
        //刪除
        user.deleteById();
        log.info("刪除結束");

    }
    
    @Test
    public void testSelect() {
        User user = new User();
        user.setCode("201");
        user.setName("oKong-selecdt");
        
        user.insert();
        
        log.info("查詢:{}",user.selectById());
    }
}

 

以上就列舉了常用的,官方提供了很多的通用方法:

通用方法

注意控制台的sql輸出,對比下就知道各方法之間的區別了。


對於通用代碼如何注入的,可查看com.baomidou.mybatisplus.mapper.AutoSqlInjector類,這個就是注入通用的CURD方法的類。

AutoSqlInjector類


條件構造器

在通用的CURD無法滿足時,這個時候 強大的條件構造器就排上用場了。主要提供了實體包裝器,用於處理 sql 拼接,排序,實體參數查詢等!

這里需要注意:使用的是數據庫字段,不是Java屬性!,原來使用另一款通用mapper時記得使用的是JAVA屬性。

sql條件拼接

這也是條件構造器最靈活的地方了。

ConditionTest.java

@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用於測試的注解,可指定啟動類或者測試環境等,這里直接默認。
@SpringBootTest 
@Slf4j
public class ConditionTest {
    
    @Autowired
    IUserService userService;
    
    @Test
    public void testOne() {
        User user =  new User();
        user.setCode("701");
        user.setName("okong-condition");
        user.insert();
        
        EntityWrapper<User> qryWrapper = new EntityWrapper<>();
        
        qryWrapper.eq(User.CODE, user.getCode());
        qryWrapper.eq(User.NAME, user.getName());
        
        //也可以直接 
//        qryWrapper.setEntity(user);
        
        //打印sql語句
        System.out.println(qryWrapper.getSqlSegment());
        
        //設置select 字段 即:select code,name from 
        qryWrapper.setSqlSelect(User.CODE,User.NAME);
        System.out.println(qryWrapper.getSqlSelect());
        
        //查詢
        User qryUser = userService.selectOne(qryWrapper);
        System.out.println(qryUser);
        log.info("拼接一結束");
    }
    
    @Test
    public void testTwo() {
        User user =  new User();
        user.setCode("702");
        user.setName("okong-condition");
        user.insert();
        
        EntityWrapper<User> qryWrapper = new EntityWrapper<>();
        qryWrapper.where("code = {0}", user.getCode())
        .and("name = {0}",user.getName())
        .andNew("status = 0");
        System.out.println(qryWrapper.getSqlSegment());
        //等等很復雜的。
        //復雜的建議直接寫在xml里面了,要是非動態的話 比較xml一眼看得懂呀
        //查詢
        User qryUser = userService.selectOne(qryWrapper);
        System.out.println(qryUser);
        log.info("拼接二結束");
    }

}

 

com.baomidou.mybatisplus.mapper.Wrapper<T>類還有很多的方法,大家可以試試。
wrapper

條件參數說明

查詢方式 說明
setSqlSelect 設置 SELECT 查詢字段
where WHERE 語句,拼接 + WHERE 條件
and AND 語句,拼接 + AND 字段=值
andNew AND 語句,拼接 + AND (字段=值)
or OR 語句,拼接 + OR 字段=值
orNew OR 語句,拼接 + OR (字段=值)
eq 等於=
allEq 基於 map 內容等於=
ne 不等於<>
gt 大於>
ge 大於等於>=
lt 小於<
le 小於等於<=
like 模糊查詢 LIKE
notLike 模糊查詢 NOT LIKE
in IN 查詢
notIn NOT IN 查詢
isNull NULL 值查詢
isNotNull IS NOT NULL
groupBy 分組 GROUP BY
having HAVING 關鍵詞
orderBy 排序 ORDER BY
orderAsc ASC 排序 ORDER BY
orderDesc DESC 排序 ORDER BY
exists EXISTS 條件語句
notExists NOT EXISTS 條件語句
between BETWEEN 條件語句
notBetween NOT BETWEEN 條件語句
addFilter 自由拼接 SQL
last 拼接在最后,例如:last("LIMIT 1")

自定義SQL使用條件構造器

UserDao.java加入接口方法:

/**
     * 
     * @param rowBounds 分頁對象 直接傳入page即可
     * @param wrapper 條件構造器
     * @return
     */
    List<User> selectUserWrapper(RowBounds rowBounds, @Param("ew") Wrapper<User> wrapper);

 

UserMapper.xml加入對應的xml節點:

    <!-- 條件構造器形式 -->
    <select id="selectUserWrapper" resultType="user">
        SELECT
        <include refid="Base_Column_List" />
        FROM USER
        <where>
            ${ew.sqlSegment}
        </where>
    </select>

 

測試類:

@Test
    public void testCustomSql() {
        User user = new User();
        user.setCode("703");
        user.setName("okong-condition");
        user.insert();
        
        EntityWrapper<User> qryWrapper = new EntityWrapper<>();
        qryWrapper.eq(User.CODE, user.getCode());
        
        Page<User> pageUser = new Page<>();
        pageUser.setCurrent(1);
        pageUser.setSize(10);
        
        List<User> userlist = userDao.selectUserWrapper(pageUser, qryWrapper);
        System.out.println(userlist.get(0));
        log.info("自定義sql結束");
    }

 

xml形式使用wrapper

UserDao.java

/**
     * 
     * @param rowBounds 分頁對象 直接傳入page即可
     * @param wrapper 條件構造器
     * @return
     */
    List<User> selectUserWrapper(RowBounds rowBounds, @Param("ew") Wrapper<User> wrapper);

 

對應的UserMapper.xml:

    <!-- 條件構造器形式 -->
    <select id="selectUserWrapper" resultType="user">
        SELECT
        <include refid="Base_Column_List" />
        FROM USER
        <where>
            ${ew.sqlSegment}
        </where>
    </select>

自定義SQL語句

在一些需要多表關聯時,條件構造器和通用CURD都無法滿足時,還可以自行手寫sql語句進行擴展。注意:這都是mybatis的用法。

以下兩種方式都是改造UserDao接口。

注解形式

@Select("SELECT * FROM USER WHERE CODE = #{userCode}")
    List<User> selectUserCustomParamsByAnno(@Param("userCode")String userCode);

 

xml形式

List<User> selectUserCustomParamsByXml(@Param("userCode")String userCode);

 

同時,UserMapper.xml新增一個節點:

    <!-- 由於設置了別名:typeAliasesPackage=cn.lqdev.learning.mybatisplus.samples.biz.entity,所以resultType可以不寫全路徑了。 -->
    <select id="selectUserCustomParamsByXml" resultType="user">
        SELECT 
        <include refid="Base_Column_List"/> 
        FROM USER 
       WHERE CODE = #{userCode}
    </select>

 

測試類CustomSqlTest.java

@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用於測試的注解,可指定啟動類或者測試環境等,這里直接默認。
@SpringBootTest 
@Slf4j
public class CustomSqlTest {
    
    @Autowired
    UserDao userDao;
    
    @Test
    public void testCustomAnno() {
        User user = new User();
        user.setCode("901");
        user.setName("okong-sql");
        user.insert();
        List<User> userlist = userDao.selectUserCustomParamsByAnno(user.getCode());
        //由於新增的 肯定不為null 故不判斷了。
        System.out.println(userlist.get(0).toString());
        log.info("注解形式結束------");
    }
    
    @Test
    public void testCustomXml() {
        User user = new User();
        user.setCode("902");
        user.setName("okong-sql");
        user.insert();
        List<User> userlist = userDao.selectUserCustomParamsByXml(user.getCode());
        //由於新增的 肯定不為null 故不判斷了。
        System.out.println(userlist.get(0).toString());
        log.info("xml形式結束------");
    }

}

注意事項

在使用spring-boot-maven-plugin插件打包成springboot運行jar時,需要注意下,由於springboot的jar掃描路徑方式問題,會導致別名的包未掃描到,所以這個只需要把mybatis默認的掃描設置為SpringbootVFS實現。

直接修改spring-mybatis.xml文件:

  <!--mybatis-->
    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 自動掃描mapper.xml文件,支持通配符 -->
        <property name="mapperLocations" value="classpath:mapper/**/*.xml"/>
        <!-- 配置文件,比如參數配置(是否啟動駝峰等)、插件配置等 -->
        <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
        <!-- 啟用別名,這樣就無需寫全路徑類名了,具體可自行查閱資料 -->
        <property name="typeAliasesPackage" value="cn.lqdev.learning.mybatisplus.samples.biz.entity"/>
        <!-- MP 全局配置注入 -->
        <property name="globalConfig" ref="globalConfig"/>
        <!-- 設置vfs實現,避免路徑掃描問題 -->
        <property name="vfs"  value="com.baomidou.mybatisplus.spring.boot.starter.SpringBootVFS"></property>
    </bean>

 

分頁插件、性能分析插件

mybatis的插件機制使用起來是很簡單的,只需要注冊即可。

mybatis-config.xml

    <plugins>
      <!-- SQL 執行性能分析,開發環境使用,線上不推薦。 -->
      <plugin interceptor="com.baomidou.mybatisplus.plugins.PerformanceInterceptor"></plugin>
      <!-- 分頁插件配置 -->
      <plugin interceptor="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></plugin>
    </plugins>

 

分頁測試類(性能分析,配置后可以輸出sql及取數時間):

@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用於測試的注解,可指定啟動類或者測試環境等,這里直接默認。
@SpringBootTest 
@Slf4j
public class PluginTest {
    
    @Autowired
    IUserService userService;
    
    @Test
    public void testPagination() {
        Page<User> page = new Page<>();
        //每頁數
        page.setSize(10);
        //當前頁碼
        page.setCurrent(1);
        
        //無條件時
        Page<User> pageList = userService.selectPage(page);
        System.out.println(pageList.getRecords().get(0));
        
        //新增數據 避免查詢不到數據
        User user = new User();
        user.setCode("801");
        user.setName("okong-Pagination");
        user.insert();
        //加入條件構造器
        EntityWrapper<User> qryWapper = new EntityWrapper<>();
        //這里也能直接設置 entity 這是條件就是entity的非空字段值了
//        qryWapper.setEntity(user);
        //這里建議直接用 常量 
    //    qryWapper.eq(User.CODE, user.getCode());
        pageList = userService.selectPage(page, qryWapper);
        System.out.println(pageList.getRecords().get(0));
        log.info("分頁結束");
    }

}

 

性能插件體現,控制台輸出:

 Time:4 ms - ID:cn.lqdev.learning.mybatisplus.samples.biz.dao.UserDao.selectPage
 Execute SQL: SELECT id AS id,code,`name`,`status`,gmt_create AS gmtCreate,gmt_modified AS gmtModified FROM user WHERE id=1026120705692434433 AND code='801' AND `name`='okong-Pagination' LIMIT 0,10 

公共字段自動填充

通常,每個公司都有自己的表定義,在《阿里巴巴_Java_開發手冊》中,就強制規定表必備三字段:id, gmt_create, gmt_modified。所以通常我們都會寫個公共的攔截器去實現自動填充比如創建時間和更新時間的,無需開發人員手動設置。而在MP中就提供了這么一個公共字段自動填充功能

設置填充字段的填充類型

User.java

 /**
     * 創建時間
     */
    @TableField(fill=FieldFill.INSERT)
    private Date gmtCreate;
    /**
     * 修改時間
     */
    @TableField(fill=FieldFill.INSERT_UPDATE)
    private Date gmtModified;

 

注意這里是可以在代碼生成器里面配置規則的,可自動配置,詳見代碼生成器類。

定義處理類

MybatisObjectHandler.java

public class MybatisObjectHandler extends MetaObjectHandler{

    @Override
    public void insertFill(MetaObject metaObject) {
        //新增時填充的字段
        setFieldValByName("gmtCreate", new Date(), metaObject);
        setFieldValByName("gmtModified", new Date(), metaObject);
        
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        //更新時 需要填充字段
        setFieldValByName("gmtModified", new Date(), metaObject);
    }
}

 

同時修改springb-mybatis.xml文件,加入此配置:

    <bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
        <!--
            AUTO->`0`("數據庫ID自增")QW
             INPUT->`1`(用戶輸入ID")
            ID_WORKER->`2`("全局唯一ID")
            UUID->`3`("全局唯一ID")
        -->
        <property name="idType" value="2" />
        <property name="metaObjectHandler" ref="mybatisObjectHandler"></property>
    </bean>
    
    <bean id="mybatisObjectHandler" class="cn.lqdev.learning.mybatisplus.samples.config.MybatisObjectHandler"/>

 

這個時候再新增或者修改,對應時間就會進行更新了。

 Time:31 ms - ID:cn.lqdev.learning.mybatisplus.samples.biz.dao.UserDao.insert
 Execute SQL: INSERT INTO user ( id, code, `name`, gmt_create,gmt_modified ) VALUES ( 1026135016838037506, '702', 'okong-condition', '2018-08-05 23:57:07.344','2018-08-05 23:57:07.344' )

相關資料

  1. MP官網:http://mp.baomidou.com

總結

本文主要列舉了開發過程中常用的操作數據庫的方法及相關配置。應該可以應付百分之八十以上的需求了吧。之后有時間,會進行補充的,比如自定義插件、大批量數據的寫法等。

轉載於:https://www.cnblogs.com/okong/p/mybatis-plus-guide-one.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM