广告
返回顶部
首页 > 资讯 > 精选 >Spring Data JPA映射怎么自定义实体类
  • 714
分享到

Spring Data JPA映射怎么自定义实体类

2023-06-25 17:06:57 714人浏览 泡泡鱼
摘要

这篇文章主要介绍“spring Data JPA映射怎么自定义实体类”,在日常操作中,相信很多人在Spring Data JPA映射怎么自定义实体类问题上存在疑惑,小编查阅了各式资料,整理出简单好用的

这篇文章主要介绍“spring Data JPA映射怎么自定义实体类”,在日常操作中,相信很多人在Spring Data JPA映射怎么自定义实体类问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Spring Data JPA映射怎么自定义实体类”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

Spring Data JPA映射自定义实体类

这个问题困扰了我2天=-=,好像也能使用 jpql解决

先说下自己的功能:查询oracle最近sql执行记录

sql很简单:【如果需要分页,需要自己手动分页,因为你使用分页工具他第一页查询不会查询rownum,第二页查询就会查询rownum,然而这个返回的List<Object[]>中的参数必须要和实体类中一一对应,所以这就有一个不可控制的属性rownum,所以我们不能使用Pageable入参接口了,需要自定义pageSize pageNum参数】

SELECT t.SQL_ID AS SQL的ID, t.SQL_TEXT AS SQL语句, t.HASH_VALUE AS 完整SQL哈希值, t.ELAPSED_TIME AS 解析执行总共时间微秒, t.EXECUTioNS AS 执行总共次数, t.LAST_ACTIVE_TIME AS 执行最后时间, t.CPU_TIME AS CPU执行时间微秒 FROM  v$sqlarea t WHERE  t.PARSING_SCHEMA_NAME IN ( 'C##DBAAS' )  AND t.EXECUTIONS > 10   AND t.LAST_ACTIVE_TIME > TO_DATE('0001-01-01 01:01:01', 'yyyy-MM-dd hh34:mi:ss')  AND t.ELAPSED_TIME > 0  AND t.CPU_TIME > 0 ORDER BY  t.EXECUTIONS DESC;

但是我用的是Spring Data JPA。。。。这个网上说不能将查询结果自动映射到自定义的实体类。。。。这就比较蛋疼了,在网上就找了个轮子。先上一下自己的Dao层,查出来的是集合数组,所以使用List< Object [ ] >接收【我将sql简化了一下,主要先测试能不能成功】

@Query(value="SELECT\r\n" +    " t.SQL_ID,\r\n" +    " t.ELAPSED_TIME,\r\n" +    " t.EXECUTIONS,\r\n" +    " t.LAST_ACTIVE_TIME, \r\n" +    " t.CPU_TIME \r\n" +    "FROM\r\n" +    " v$sqlarea t \r\n" +    "WHERE\r\n" +    " t.PARSING_SCHEMA_NAME IN ( 'C##DBAAS' )   AND t.EXECUTIONS > 100     \r\n" +    "ORDER BY\r\n" +    " t.EXECUTIONS DESC",nativeQuery=true) public List<Object[]> findTopSQLS4();

然后就是实体类了:注意实体类中 必须包含构造函数,而且构造函数中的参数必须和你SQL中 查询的参数 顺序保持一致

package com.befery.oams.entity; import java.io.Serializable;import java.math.BigInteger;import java.security.Timestamp;import java.util.Date; import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.Id;import javax.persistence.Table; @Entity@Table(name = "v$sqlarea")public class V$sqlarea implements Serializable {  @Id private String sqlId;  private Number elapsedTime; // 解析+执行sql 总时间 微秒 private Number executions; // 执行次数 private Date lastActiveTime; private Number cpuTime; public String getSqlId() {  return sqlId; }  public void setSqlId(String sqlId) {  this.sqlId = sqlId; }  public Number getElapsedTime() {  return elapsedTime; }  public void setElapsedTime(Number elapsedTime) {  this.elapsedTime = elapsedTime; }  public Number getExecutions() {  return executions; }  public void setExecutions(Number executions) {  this.executions = executions; }  public Date getLastActiveTime() {  return lastActiveTime; }  public void setLastActiveTime(Date lastActiveTime) {  this.lastActiveTime = lastActiveTime; }  public Number getcpuTime() {  return cpuTime; }  public void setCpuTime(Number cpuTime) {  this.cpuTime = cpuTime; }  public V$sqlarea() { }  public V$sqlarea(String sqlId, Number elapsedTime, Number executions, Date lastActiveTime,Number cpuTime) {  this.sqlId = sqlId;  this.elapsedTime = elapsedTime;  this.executions = executions;  this.lastActiveTime = lastActiveTime;  this.cpuTime = cpuTime; } }

然后就是网上的轮子了

package com.befery.oams.util; import org.slf4j.Logger;import org.slf4j.LoggerFactory; import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map; public class EntityUtils {    private static Logger logger = LoggerFactory.getLogger(EntityUtils.class);         public static <T> List<T> castEntity(List<Object[]> list, Class<T> clazz, Object model) {        List<T> returnList = new ArrayList<T>();        if (list.isEmpty()) {            return returnList;        }        //获取每个数组集合的元素个数        Object[] co = list.get(0);         //获取当前实体类的属性名、属性值、属性类别        List<Map> attributeInfoList = getFiledsInfo(model);        //创建属性类别数组        Class[] c2 = new Class[attributeInfoList.size()];        //如果数组集合元素个数与实体类属性个数不一致则发生错误        if (attributeInfoList.size() != co.length) {            return returnList;        }        //确定构造方法        for (int i = 0; i < attributeInfoList.size(); i++) {            c2[i] = (Class) attributeInfoList.get(i).get("type");        }        try {            for (Object[] o : list) {                Constructor<T> constructor = clazz.getConstructor(c2);                returnList.add(constructor.newInstance(o));            }        } catch (Exception ex) {            logger.error("实体数据转化为实体类发生异常:异常信息:{}", ex.getMessage());            return returnList;        }        return returnList;    }         private static Object getFieldValueByName(String fieldName, Object modle) {        try {            String firstLetter = fieldName.substring(0, 1).toUpperCase();            String getter = "get" + firstLetter + fieldName.substring(1);            Method method = modle.getClass().getMethod(getter, new Class[]{});            Object value = method.invoke(modle, new Object[]{});            return value;        } catch (Exception e) {            return null;        }    }         private static List<Map> getFiledsInfo(Object model) {        Field[] fields = model.getClass().getDeclaredFields();        List<Map> list = new ArrayList(fields.length);        Map infoMap = null;        for (int i = 0; i < fields.length; i++) {            infoMap = new HashMap(3);            infoMap.put("type", fields[i].getType());            infoMap.put("name", fields[i].getName());            infoMap.put("value", getFieldValueByName(fields[i].getName(), model));            list.add(infoMap);        }        return list;    }}

最后的操作,调用 castEntity() 方法:

@GetMapping(value = "/list") @ResponseBody public List<V$sqlarea> selectTopSQLUntreated() {  System.out.println("============================TOPSQL       START=================================");  List<Object[]> list = v$sqlareaDao.findTopSQLS4();  List<V$sqlarea> list1 =EntityUtils.castEntity(list, V$sqlarea.class,new V$sqlarea());  System.out.println("============================TOPSQL       END=================================");  return list1; }

看一下日志的输出

============================TOPSQL START=================================
Hibernate:
SELECT
t.SQL_ID,
t.ELAPSED_TIME,
t.EXECUTIONS,
t.LAST_ACTIVE_TIME,
t.CPU_TIME
FROM
v$sqlarea t
WHERE
t.PARSING_SCHEMA_NAME IN (
'C##DBAAS'
)
AND t.EXECUTIONS > 100
ORDER BY
t.EXECUTIONS DESC
============================TOPSQL END=================================
2019-03-12 18:06:57.108 INFO 21076 --- [NIO-8081-exec-7] com.befery.oams.config.LogAspectConfig : --------------返回内容----------------
2019-03-12 18:06:57.114 INFO 21076 --- [nio-8081-exec-7] com.befery.oams.config.LogAspectConfig : Response内容:[{"cpuTime":84731,"elapsedTime":183491,"executions":348,"lastActiveTime":1552385204000,"sqlId":"f05fn7j6rbcsj"},{"cpuTime":17827,"elapsedTime":33036,"executions":212,"lastActiveTime":1552385203000,"sqlId":"avc1jqzz04wpr"},{"cpuTime":9054,"elapsedTime":23874,"executions":174,"lastActiveTime":1552385204000,"sqlId":"b4xr1nw5vtk2v"},{"cpuTime":102017,"elapsedTime":97842,"executions":153,"lastActiveTime":1552313331000,"sqlId":"711b9thj3s4ug"},{"cpuTime":89011,"elapsedTime":90341,"executions":153,"lastActiveTime":1552313331000,"sqlId":"grqh2qs9ajypn"},{"cpuTime":58984,"elapsedTime":81214,"executions":135,"lastActiveTime":1552385214000,"sqlId":"d442vk7001fvw"},{"cpuTime":17260604818,"elapsedTime":41375561059,"executions":122,"lastActiveTime":1552297847000,"sqlId":"170am4cyckruf"},{"cpuTime":13194,"elapsedTime":31267,"executions":108,"lastActiveTime":1552383540000,"sqlId":"9q00dg3n0748y"}]
2019-03-12 18:06:57.114 INFO 21076 --- [nio-8081-exec-7] com.befery.oams.config.LogAspectConfig : --------------返回内容----------------

JPA 配置类实体映射示例

A: 两张表示例

@Entity@Table(name="apo_config")public class SiteConfig implements Serializable {    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    private Long ID;    private String caption;    @ElementCollection(fetch = FetchType.LAZY)    @MapKeyColumn(name="name")    @Column(name="value")    @CollectionTable(name="apo_config_attributes", joinColumns=@JoinColumn(name="ca_id"))    private Map<String, String> attributes = new HashMap<String, String>();     //GET/SET}

测试代码

@Test    public void test(){        SiteConfig sc=new SiteConfig();        sc.setID(1L);        sc.setCaption("全局配置");        Map<String, String> data=new HashMap<>();        data.put("site", "csdn.net");        data.put("account", "xiaofanku");        sc.setAttributes(data);        siteConfigDao.save(sc);    }    @Test    public void getConfig(){        SiteConfig config=siteConfigDao.findOne(1L);        assertEquals(config.getAttributes().get("site"), "csdn.net");    }

apo_config:表结构

Spring Data JPA映射怎么自定义实体类

apo_config_attributes:表结构

Spring Data JPA映射怎么自定义实体类

B: 三张表示例

@Entity@Table(name="apo_config")public class SiteConfig implements Serializable {    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    private Long ID;    private String caption;    @OneToMany(cascade=CascadeType.ALL, orphanRemoval = true)    @MapKey(name="name")    @JoinTable(name = "apo_config_attributes")    private Map<String, ConfigAttribute> attributes=new HashMap<>();    //GET/SET}
@Entity@Table(name="apo_attributes")public class ConfigAttribute implements Serializable {    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    private Long ID;    @Column(name="name")    private String name;    private String value;    //GET/SET}

测试代码

@Test @Ignore    public void test(){        SiteConfig sc=new SiteConfig();        sc.setID(1L);        sc.setCaption("全局配置");        Map<String, ConfigAttribute> data=new HashMap<>();        ConfigAttribute ca1=new ConfigAttribute();        ca1.setName("site");ca1.setValue("csdn.net");        data.put("site", ca1);        ConfigAttribute ca2=new ConfigAttribute();        ca2.setName("account");ca2.setValue("xiaofanku");        data.put("account", ca2);        sc.setAttributes(data);        siteConfigDao.save(sc);    }    @Test @Ignore    public void getConfig(){        SiteConfig config=siteConfigDao.findOne(1L);        assertEquals(config.getAttributes().get("site").getValue(), "csdn.net");    }

apo_config:表结构

Spring Data JPA映射怎么自定义实体类

apo_attributes:表结构

Spring Data JPA映射怎么自定义实体类

apo_config_attributes:中间表结构

Spring Data JPA映射怎么自定义实体类

C: 使用ASF Commons BeanUtils来构造一个Dynamic Class

import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.Objects;import java.util.Set;import org.apache.commons.beanutils.BasicDynaClass;import org.apache.commons.beanutils.ConvertUtils;import org.apache.commons.beanutils.DynaBean;import org.apache.commons.beanutils.DynaProperty;public class DynamicClass{    private final DynaBean config;        public DynamicClass(Map<String,String> attributeMeta) throws IllegalAccessException, InstantiationException, ClassNotFoundException{        DynaProperty[] props=covert(attributeMeta).toArray(new DynaProperty[]{});        BasicDynaClass dynaClass = new BasicDynaClass("CustomConfig", null, props);        this.config = dynaClass.newInstance();    }        public DynamicClass(Set<Attribute> attributes) throws ClassNotFoundException, IllegalAccessException, InstantiationException{        DynaProperty[] props=covert(attributes).toArray(new DynaProperty[]{});        BasicDynaClass dynaClass = new BasicDynaClass("CustomConfig", null, props);        this.config = dynaClass.newInstance();        load(attributes);    }        public Object getValue(String attributeName){        return config.get(attributeName);    }        public <T> T getValue(String attributeName, Class<T> classType) throws java.lang.ClassCastException{        return (T)getValue(attributeName);    }        public void setValue(String attributeName, String attributeValue){        DynaProperty dp = config.getDynaClass().getDynaProperty(attributeName);        config.set(attributeName, ConvertUtils.convert(attributeValue, dp.getType()));    }        public void setValue(Attribute attribute) throws ClassNotFoundException {        config.set(attribute.getName(), ConvertUtils.convert(attribute.getValue(), Class.forName(attribute.getClassName())));    }        private void load(Set<Attribute> attributes){        for(Attribute attr : attributes){            try{                config.set(attr.getName(), ConvertUtils.convert(attr.getValue(), Class.forName(attr.getClassName())));            }catch(ClassNotFoundException e){            }        }    }        private List<DynaProperty> covert(Set<Attribute> attributes) throws ClassNotFoundException{        List<DynaProperty> attres=new ArrayList<>();        for(Attribute attr : attributes){            attres.add(new DynaProperty(attr.getName(), Class.forName(attr.getClassName())));        }        return attres;    }        private List<DynaProperty> covert(Map<String,String> attributeMeta) throws ClassNotFoundException{        List<DynaProperty> properties=new ArrayList<>();        Set<String> attrSet=attributeMeta.keySet();        for(String attrName : attrSet){            String className=attributeMeta.get(attrName);            properties.add(new DynaProperty(attrName, Class.forName(className)));        }        return properties;    }    public static enum Type{        BOOLEAN("java.lang.Boolean"),        INTEGER("java.lang.Integer"),        LONG("java.lang.Long"),        STRING("java.lang.String"),        CHAR("java.lang.Character"),        DOUBLE("java.lang.Double"),        FLOAT("java.lang.Float");        private final String name;        private Type(String className){            this.name=className;        }        public String getName() {            return name;        }    }    public static class Attribute{        //属性名,例:show        private final String name;        //属性名的值,例:"true"        private final String value;        //属性名的类型,例:java.lang.Boolean        private final String className;        public Attribute(String name, String value, String className) {            this.name = name;            this.value = value;            this.className = className;        }        public String getName() {            return name;        }        public String getValue() {            return value;        }        public String getClassName() {            return className;        }        @Override        public int hashCode() {            int hash = 5;            hash = 97 * hash + Objects.hashCode(this.name);            hash = 97 * hash + Objects.hashCode(this.className);            return hash;        }        @Override        public boolean equals(Object obj) {            if (this == obj) {                return true;            }            if (obj == null) {                return false;            }            if (getClass() != obj.getClass()) {                return false;            }            final Attribute other = (Attribute) obj;            if (!Objects.equals(this.name, other.name)) {                return false;            }            if (!Objects.equals(this.className, other.className)) {                return false;            }            return true;        }    }}

测试代码:

@Test    public void test(){        Set<Attribute> sas=new HashSet<>();        sas.add(new Attribute("loGo", "logo.png", DynamicClass.Type.STRING.getName()));        sas.add(new Attribute("pageSize", "50", DynamicClass.Type.INTEGER.getName()));        sas.add(new Attribute("shortcut", "true", DynamicClass.Type.BOOLEAN.getName()));        try{            DynamicClass dc=new DynamicClass(sas);            Integer ps = dc.getValue("pageSize", Integer.class);            System.out.println(ps);            dc.setValue("pageSize", "150");            System.out.println(dc.getValue("pageSize"));        }catch(Exception e){            e.printStackTrace();        }    }    @Test @Ignore    public void base() {        Map<String, String> am = new HashMap<>();        am.put("logo", DynamicClass.Type.STRING.getName());        am.put("pageSize", DynamicClass.Type.INTEGER.getName());        am.put("shortcut", DynamicClass.Type.BOOLEAN.getName());        try {            DynamicClass dc = new DynamicClass(am);            dc.setValue("pageSize", "150");            System.out.println(dc.getValue("pageSize"));            dc.setValue(new Attribute("shortcut", "true", DynamicClass.Type.BOOLEAN.getName()));            System.out.println(dc.getValue("shortcut"));        } catch (IllegalAccessException | InstantiationException | ClassNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }

到此,关于“Spring Data JPA映射怎么自定义实体类”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: Spring Data JPA映射怎么自定义实体类

本文链接: https://www.lsjlt.com/news/306170.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Spring Data JPA映射怎么自定义实体类
    这篇文章主要介绍“Spring Data JPA映射怎么自定义实体类”,在日常操作中,相信很多人在Spring Data JPA映射怎么自定义实体类问题上存在疑惑,小编查阅了各式资料,整理出简单好用的...
    99+
    2023-06-25
  • Spring Data JPA映射自定义实体类操作
    目录SpringDataJPA映射自定义实体类JPA配置类实体映射示例Spring Data JPA映射自定义实体类 这个问题困扰了我2天=-=,好像也能使用 jpql解决 先说下自...
    99+
    2022-11-12
  • Spring Data JPA怎么自定义Repository接口
    本篇内容主要讲解“Spring Data JPA怎么自定义Repository接口”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring Data J...
    99+
    2023-06-30
  • spring data jpa如何使用自定义repository实现类
    目录spring data jpa使用自定义repository实现类创建MyJpaRepository实现类创建MyJpaRepositoryFactoryBean配置JPAJpa...
    99+
    2022-11-12
  • spring data jpa 查询自定义字段,转换为自定义实体方式
    目标:查询数据库中的字段,然后转换成 JSON 格式的数据,返回前台。 环境:idea 2016.3.4, jdk 1.8, mysql 5.6, spring-boot 1.5.2...
    99+
    2022-11-12
  • Spring Data JPA框架的Repository怎么自定义
    本文小编为大家详细介绍“Spring Data JPA框架的Repository怎么自定义”,内容详细,步骤清晰,细节处理妥当,希望这篇“Spring Data JPA框架的Repository怎么自...
    99+
    2023-06-30
  • Spring Data JPA实现查询结果返回map或自定义的实体类
    目录Spring Data JPA查询结果返回map或自定义的实体类1.工具类2.具体应用spingboot:jpa:Spring data jpa 返回map 结果集Spring ...
    99+
    2022-11-12
  • Spring Data Jpa多表查询如何返回自定义实体
    小编给大家分享一下Spring Data Jpa多表查询如何返回自定义实体,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!SpringDataJ...
    99+
    2023-06-29
  • 怎么使用spring data jpa自定义sql方式
    本篇内容介绍了“怎么使用spring data jpa自定义sql方式”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!sp...
    99+
    2023-06-22
  • Spring Data Jpa如何实现自定义方法
    这篇文章将为大家详细讲解有关Spring Data Jpa如何实现自定义方法,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Spring Data Jpa 自定义方法的实现最近项目中用到...
    99+
    2023-06-22
  • spring-data-jpa怎么使用自定义repository来实现原生sql
    这篇文章给大家分享的是有关spring-data-jpa怎么使用自定义repository来实现原生sql的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。使用自定义repository实现原生sqlSpring D...
    99+
    2023-06-21
  • Spring Data JPA框架的Repository自定义实现详解
    目录1. Spring Data Repository自定义实现1.1 自定义特殊repository1.2 配置类1.3 解决歧义1.4 手动装配1.5 自定义Base Repos...
    99+
    2022-11-13
  • 关于Spring Data Jpa 自定义方法实现问题
    目录Spring Data Jpa 自定义方法的实现自己的接口主接口我新建一个类来实现我自己的接口Spring Data Jpa自定义方法关键字Spring Data Jpa 自定义...
    99+
    2022-11-12
  • spring-data-jpa使用自定义repository来实现原生sql
    目录使用自定义repository实现原生sql自定义Repository接口创建自定义RepositoryFactoryBeanSpringDataJpa原生SQL查询a.首先在S...
    99+
    2022-11-12
  • 使用Spring Data JPA如何实现自定义规则查询
    使用Spring Data JPA如何实现自定义规则查询?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。一、常用规则速查1   And    并且2   Or    &nbs...
    99+
    2023-05-31
    spring data jpa
  • MybatisPlus怎么自定义TypeHandler映射JSON类型为List
    这篇文章将为大家详细讲解有关MybatisPlus怎么自定义TypeHandler映射JSON类型为List,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。自定义TypeHandler映射JSON类型为Li...
    99+
    2023-06-28
  • Spring Data Jpa返回自定义对象的3种方法实例
    目录方法一、简单查询直接new对象方法二、Service层使用EntityManager方法三、Dao层使用Map接收自定义对象总结tasks表对应的Entity @Entity @...
    99+
    2022-11-13
  • 解决Spring Data Jpa 实体类自动创建数据库表失败问题
    目录Spring Data Jpa 实体类自动创建数据库表失败找了半天发现是一个配置的问题可能导致JPA 无法自动建表的问题汇总1、没加@Entity或引错Entity所在包2、jp...
    99+
    2022-11-12
  • JPA怎么使用nativequery多表关联查询返回自定义实体类
    这篇文章主要介绍了JPA怎么使用nativequery多表关联查询返回自定义实体类,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。JPA nativequery多表关联查询返回...
    99+
    2023-06-25
  • java怎么从clob字段映射实体类
    要从CLOB字段映射到Java实体类,可以按照以下步骤进行操作:1. 首先,创建一个Java实体类,用于表示CLOB字段的数据。例如...
    99+
    2023-10-18
    java
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作