Java框架整合,MyBatis解决Java实体类和数据库表字段不一致方法总结
分类:面向对象

company.xml

第四章 Java框架整合--切分配置文件,java切分

本章内容在第三章《Java框架整合--企业中的项目架构以及多环境分配》的代码上做修改,链接如下:

1、实现方式

将之前ssmm0-userManagement中类路径(src/main/resources)下的spring.xml切分成spring.xml和spring-data.xml两个文件,其中spring.xml依旧留在ssmm0-userManagement中类路径下,而spring-data.xml放到ssmm0-data的类路径下,切分后的位置如下图所示:

图片 1

切分前的spring.xml如下:

图片 2<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="" xmlns:xsi="" xmlns:context="" xmlns:mvc="" xsi:schemaLocation=" ; <!-- 注解扫描 --> <context:component-scan base-package="com.xxx" /> <!-- 配置fastjson转换器 --> <mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"></bean> </mvc:message-converters> </mvc:annotation-driven> <!-- 引入数据源,这里变量的读取都是从ssmm0的pom.xml中读取的 --> <bean id="xxxDataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 引入mybatis --> <bean id="xxxSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="xxxDataSource" /> </bean> <bean id="xxxMapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 这里就是包名为什么就做com.xxx.mapper.user而非com.xxx.user.mapper, 这样的话,比如说有两个项目com.xxx.mapper.user和com.xxx.mapper.hotel,value只需写作com.xxx.mapper即可 否则,value就要写作com.xxx.user.mapper,com.xxx.hotel.mapper --> <property name="basePackage" value="com.xxx.mapper" /> <property name="sqlSessionFactoryBeanName" value="xxxSqlSessionFactory" /> </bean> <!-- 配置velocity --> <bean id="velocityConfigurer" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> <property name="resourceLoaderPath"> <value>WEB-INF/templates/</value> </property> <property name="velocityProperties"> <props> <prop key="input.encoding">UTF-8</prop> <prop key="output.encoding">UTF-8</prop> </props> </property> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver"> <property name="suffix" value=".vm" /> <property name="contentType" value="text/html;charset=utf-8" /> <property name="dateToolAttribute" value="date"/> <property name="numberToolAttribute" value="number"/> </bean> </beans> View Code

切分后的spring.xml与spring-data.xml如下:

spring.xml

图片 3<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="" xmlns:xsi="" xmlns:context="" xmlns:mvc="" xsi:schemaLocation=" ; <!-- 注解扫描 --> <context:component-scan base-package="com.xxx.web" /><!-- 只扫描web就可以 --> <!-- 这里需要引入ssmm0-data项目中配置的spring-data.xml(之前不引也可以成功,忘记怎么配置的了) --> <import resource="classpath:spring-data.xml"/> <!-- 配置fastjson转换器 --> <mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"></bean> </mvc:message-converters> </mvc:annotation-driven> <!-- 配置velocity --> <bean id="velocityConfigurer" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> <property name="resourceLoaderPath"> <value>WEB-INF/templates/</value> </property> <property name="velocityProperties"> <props> <prop key="input.encoding">UTF-8</prop> <prop key="output.encoding">UTF-8</prop> </props> </property> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver"> <property name="suffix" value=".vm" /> <property name="contentType" value="text/html;charset=utf-8" /> <property name="dateToolAttribute" value="date"/> <property name="numberToolAttribute" value="number"/> </bean> </beans> View Code

spring-data.xml

图片 4<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="" xmlns:xsi="" xmlns:context="" xsi:schemaLocation=" ; <!-- 注解扫描 --> <context:component-scan base-package="com.xxx" /> <!-- 引入数据源,这里变量的读取都是从ssmm0的pom.xml中读取的 --> <bean id="xxxDataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 引入mybatis --> <bean id="xxxSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="xxxDataSource" /> </bean> <bean id="xxxMapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 这里就是包名为什么就做com.xxx.mapper.user而非com.xxx.user.mapper, 这样的话,比如说有两个项目com.xxx.mapper.user和com.xxx.mapper.hotel,value只需写作com.xxx.mapper即可 否则,value就要写作com.xxx.user.mapper,com.xxx.hotel.mapper --> <property name="basePackage" value="com.xxx.mapper" /> <property name="sqlSessionFactoryBeanName" value="xxxSqlSessionFactory" /> </bean> </beans> View Code

说明:

  • 将与controller层不直接相关的数据源与mybatis相关的配置文件分在了spring-data.xml文件中,并放置在ssmm0-data的类路径下
  • 将与controller层直接相关的fastjson转换器和velocity的配置放在了spring.xml文件中

注意:

  • spring.xml部分的注解扫描只需要扫描ssmm0-userManagement即可,而spring-data.xml处的注解扫描只需要扫描ssmm0-data中的包即可。
  • spring.xml部分需要引入spring-data.xml(但是在这之前配置的时候,并不需要引入,这一块儿有懂的朋友给哥们儿我指点一下)

2、意义

  • 将spring-data.xml放置在ssmm0-data项目中,便于我们在ssmm0-data项目中对service、dao、mapper等进行测试
  • 将来若修改数据源或者mybatis的配置,只需要修改spring-data.xml即可,而不需要修改其他每个业务模块的spring.xml,这就是将各个业务模块的spring.xml中的公共配置代码集中到一起的最大意义
  • 减少了每个业务模块中的spring.xml的重复配置代码

3、切分原则

  • 与自己模块相关的配置信息就放在自己模块下(即与ssmm0-data相关的配置就配置在ssmm0-data项目中,与ssmm-userManagement相关的配置就配置在ssmm0-userManagement中)
  • 公共的配置代码抽取出来集中在一起

注意:以第一点为重!!!

 

测试:

对于以上配置文件切分后的项目进行测试的话,要注意,先把ssmm0项目编译一下"clean compile"(见第一章),然后将ssmm0-data的项目的env改成dev(见第三章),否则使用默认的配置prod,将导致数据库连接失败,之后运行项目ssmm0-userManagement就可以了。

Java框架整合--切分配置文件,java切分 本章内容在第三章《Java框架整合--企业中的项目架构以及多环境分配》的代码上做修改,链接...

在此,首先说明一点任何持久性框架都需要解决一个问题,那就是Java实体类的字段一般来说基本上会与数据库表中字段不一致,那么它们是如何解决的呢?咱们以Hibernate和SpringJDBC为例说明一下;

数据库中Delivery表:id,recipient,phone,address,postcode,delivery_type,parcel_number,express_company,express_number

<?xml  encoding="utf-8"   company

1、Hibernate中一般通过XML映射和注解的方式解决不一致问题,看下面两个简单例子,

新建java projet项目:chapter14_hierarch

  --//http:www.hnguotong.com/com/78.dtd

注解方式:

Add Hibernate Capalities

?>

[java] view plain copy
@Entity 
@Table(name = "ACCOUNT") 
public class Account implements Serializable { 
    private static final long serialVersionUID = 1L; 
 
    @Id 
    @GeneratedValue 
    private int id; 
 
    @Column(name="fld_number") 
    private String number; 
     
    @OneToMany(mappedBy="account") 
    private Set<Client> clients; 
     
    private double balance; 
 

在项目的src目录下面:com.b510.examples包中手工编写:

<!--使用xml定义一个公司--->

XML映射文件配置方式:

Delivery.java     PostDelivery.java     ExpressDelivery.java(其中:PostDeliver.java和ExpressDelivery.java都继承Delivery.java)

<company>

[html] view plain copy
<?xml version="1.0"?> 
<!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
        "; 
 
<hibernate-mapping> 
 
    <class name="org.hibernate.test.domain.Account" table="ACCOUNT" lazy="false"> 
        <id name="id" column="ACCOUNT_ID"> 
            <generator class="native"/> 
        </id> 
 
        <many-to-one name="person" class="org.hibernate.test.domain.Person" cascade="save-update,lock" 
                     column="person_id" 
                     unique="true" 
                     not-null="true"/> 
    </class> 
 
</hibernate-mapping> 

Delivery.java

   <include  file="192.168.1.1"  name="IP"/>

2、Spring JDBC一般通过使用RowCallbackHandler和RowMapper,编写回调函数的方式处理不一致,各种方法如下所示:

代码:

   <property  name="address"  value="河南郑州航海路中州大道D2761"/>

好的,了解完上述两种方式,咱们再来看看对于同样的问题,MyBatis是如何处理的?

/**
 *
 */
package com.b510.examples;

   <property  name="name"     value="郑州国通交通交通设施有限公司"/>

对于这种问题,MyBatis主要提供了两种方式用来解决该问题;

/**
 *
 * @author XHW
 *
 * @date 2011-7-9
 *
 */
public class Delivery {

   <property  name="keywords"  value="郑州标牌厂家"/>

一、通过对查询SQL采用字段别名的方式

 private Integer id;
 private String recipient;
 private String phone;
 private String address;
 private String postcode;
 /**
  * @return the id
  */
 public Integer getId() {
  return id;
 }
 /**
  * @param id the id to set
  */
 public void setId(Integer id) {
  this.id = id;
 }
 /**
  * @return the recipient
  */
 public String getRecipient() {
  return recipient;
 }
 /**
  * @param recipient the recipient to set
  */
 public void setRecipient(String recipient) {
  this.recipient = recipient;
 }
 /**
  * @return the phone
  */
 public String getPhone() {
  return phone;
 }
 /**
  * @param phone the phone to set
  */
 public void setPhone(String phone) {
  this.phone = phone;
 }
 /**
  * @return the address
  */
 public String getAddress() {
  return address;
 }
 /**
  * @param address the address to set
  */
 public void setAddress(String address) {
  this.address = address;
 }
 /**
  * @return the postcode
  */
 public String getPostcode() {
  return postcode;
 }
 /**
  * @param postcode the postcode to set
  */
 public void setPostcode(String postcode) {
  this.postcode = postcode;
 }
 
}

   <property  name="internet"   value="--www.hnguotong.com--"/>

1、新建表和插入数据

PostDelivery.java

   <property  name="department"      ref="department"/>

[html] view plain copy
DROP TABLE IF EXISTS `sl_company`; 
CREATE TABLE `sl_company` ( 
  `company_id` int(11) NOT NULL AUTO_INCREMENT, 
  `company_name` varchar(50) DEFAULT NULL, 
  `full_name` varchar(100) DEFAULT NULL, 
  `address` varchar(100) DEFAULT NULL, 
  `post_code` varchar(45) DEFAULT NULL, 
   PRIMARY KEY (`company_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 
 
-- ---------------------------- 
-- Records of sl_company 
-- ---------------------------- 
INSERT INTO `sl_company` VALUES ('1', '锐客科技', '上海锐客科技股份有限公司', '上海市浦东新区峨山路91弄97号陆家嘴软件园5号楼3层', '200127'); 

代码:

   <department  extends="default.xml"  name="jfei">

2、创建对应的实体类

/**
 *
 */
package com.b510.examples;

     <property  name="name" value="office,service,internet"/>

[java] view plain copy
package com.mybatis.entity; 
 
import java.io.Serializable; 
 
public class SlCompany implements Serializable { 
 
    private static final long serialVersionUID = 1L; 
 
    private int companyId; 
 
    private String companyName; 
 
    private String fullName; 
 
    private String address; 
 
    private String postCode; 
 
    public SlCompany() { 
        super(); 
    } 
 
    public int getCompanyId() { 
        return companyId; 
    } 
 
    public void setCompanyId(int companyId) { 
        this.companyId = companyId; 
    } 
 
    public String getCompanyName() { 
        return companyName; 
    } 
 
    public void setCompanyName(String companyName) { 
        this.companyName = companyName; 
    } 
 
    public String getFullName() { 
        return fullName; 
    } 
 
    public void setFullName(String fullName) { 
        this.fullName = fullName; 
    } 
 
    public String getAddress() { 
        return address; 
    } 
 
    public void setAddress(String address) { 
        this.address = address; 
    } 
 
    public String getPostCode() { 
        return postCode; 
    } 
 
    public void setPostCode(String postCode) { 
        this.postCode = postCode; 
    } 
     

/**
 *
 * @author XHW
 *
 * @date 2011-7-9
 *
 */
public class PostDelivery extends Delivery{

    </department>

3、上述创建完成后可以很明显发现字段名称与属性名称不一致,继续看如何解决该问题,找到slCompanyMapper.xml文件,修改文件内容如下:

 private String parcelNumber;

    <mapping  resource="com/jfei/jfei.xml"/>

[html] view plain copy
<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "; 
<mapper namespace="com.mybatis.mappings.slCompanyMapper"> 
    <!-- 通过使用字段别名的方式,AS关键字前面的是数据库表的字段名,后面对应的Java实体类对应的属性名称 --> 
    <select id="getSlCompany" parameterType="int" 
        resultType="com.mybatis.entity.SlCompany"> 
        SELECT company_id AS companyId,company_name AS 
        companyName,full_name AS fullName, post_code AS postCode, address FROM 
        sl_company WHERE company_id=#{id} 
    </select> 
</mapper> 

 /**
  * @return the parcelNumber
  */
 public String getparcelNumber() {
  return parcelNumber;
 }

</company>

4、执行测试类,发现通过上述方式,仍然执行出既定结果,需要说明的是如果字段过多、过长,我们可以继续采用下面方式,将字段列表单独映射出来,用的时候直接导入即可,如下所示:

 /**
  * @param parcelNumber the parcelNumber to set
  */
 public void setparcelNumber(String parcelNumber) {
  this.parcelNumber = parcelNumber;
 } 
}

<!-- jfei.xml(介绍某个人的性格,职位)  -->

[html] view plain copy
<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "; 
<mapper namespace="com.mybatis.mappings.slCompanyMapper"> 
    <!-- 通过使用字段别名的方式,AS关键字前面的是数据库表的字段名,后面对应的Java实体类对应的属性名称 --> 
    <!-- SlCompany字段映射列表 --> 
    <sql id="getSlCompanyColumns"> 
        company_id AS companyId, 
        company_name AS companyName, 
        full_name AS fullName,  
        post_code AS postCode,  
        address  
    </sql> 
    <!-- 通过使用include元素的refid属性引入上面自定义的映射列表 --> 
    <select id="getSlCompany" parameterType="int" resultType="com.mybatis.entity.SlCompany"> 
        SELECT <include refid="getSlCompanyColumns"/> FROM    sl_company WHERE company_id=#{id} 
    </select> 
</mapper> 

ExpressDelivery.java

<?xml   encoding="utf-8"  position

二、通过在xml中自定义结果映射的方式
1、表结构与java实体类同上,不在赘述。

代码:

  //

2、下面来看看映射xml中的变化;

/**
 *
 */
package com.b510.examples;

?>

[html] view plain copy
<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "; 
<mapper namespace="com.mybatis.mappings.slCompanyMapper"> 
    <!-- 通过使用结果映射的方式 --> 
    <select id="getSlCompany2" parameterType="int" resultMap="slCompanyResultMap"> 
        SELECT * FROM sl_company WHERE company_id=#{id} 
    </select> 
     
    <!-- 通过使用resultMap元素自定义结果映射,该元素内部的id和result提供了javaType、jdbcType、typeHandler属性满足类型要求 --> 
    <resultMap type="com.mybatis.entity.SlCompany" id="slCompanyResultMap"> 
        <!-- 通过id元素映射主键类型 --> 
        <id column="company_id" property="companyId" /> 
        <!-- 通过result元素映射非主键类型 --> 
        <result column="company_name" property="companyName"/> 
        <result column="full_name" property="fullName"/> 
        <!-- 当字段名和属性名一致是,可写可不写 --> 
        <result column="address" property="address"/> 
        <result column="post_code" property="postCode"/> 
    </resultMap> 
     
</mapper> 

/**
 *
 * @author XHW
 *
 * @date 2011-7-9
 *
 */
public class ExpressDelivery extends Delivery {

<position>

3、执行简单输出类

 private String expressNumber;
 private String expressCompany;
 /**
  * @return the expressNumber
  */
 public String getExpressNumber() {
  return expressNumber;
 }
 /**
  * @param expressNumber the expressNumber to set
  */
 public void setExpressNumber(String expressNumber) {
  this.expressNumber = expressNumber;
 }
 /**
  * @return the expressCompany
  */
 public String getExpressCompany() {
  return expressCompany;
 }
 /**
  * @param expressCompany the expressCompany to set
  */
 public void setExpressCompany(String expressCompany) {
  this.expressCompany = expressCompany;
 }
}

   <property  name="age" value="23"/>

[java] view plain copy
package com.mybatis.test; 
 
import java.io.IOException; 
import java.io.InputStream; 
 
import org.apache.ibatis.io.Resources; 
import org.apache.ibatis.session.SqlSession; 
import org.apache.ibatis.session.SqlSessionFactory; 
import org.apache.ibatis.session.SqlSessionFactoryBuilder; 
 
import com.mybatis.entity.SlCompany; 
 
public class MyBatisTest { 
     
    public static void main(String[] args) throws IOException { 
         
        // mybatis提供了Resources类用来提供简易方式来获取各种来源的资源文件信息 
        // 获取mybatis.xml的字节流 
        InputStream is = Resources.getResourceAsStream("mybatis.xml"); 
        // 获取mybatis.xml的字符流 
        //Reader reader = Resources.getResourceAsReader("mybatis.xml"); 
         
        // 构建SqlSessionFactory对象,传入字符流或字节流均可 
        SqlSessionFactory SqlSessionFactory = new SqlSessionFactoryBuilder().build(is); 
        // 获取SqlSession对象 
        SqlSession sqlSession = SqlSessionFactory.openSession(); 
         
        // 执行查询操作 
        // selectOne()方法的第一个参数是由slCompanyMapper.xml里配置的mapper的namespace属性

用Hibernate逆向工程单独生成Delivery.hbm.xml映射文件:

   <property  name="name" value="jfei"/>

  • 该mapper下select元素的id属性值组成的 
            SlCompany slCompany = sqlSession.selectOne("com.mybatis.mappings.slCompanyMapper.getSlCompany2", 1); 
             
            // 用完SqlSession之后,不要忘记关闭 
            sqlSession.close(); 
             
            System.out.println(slCompany.getAddress()); 
        } 

Delivery.hbm.xml

   <property  name="sex"        value="famle"/>

输出既定信息;
总结:无论上述哪一种方式都提供了一种解决不一致的途径,需要注意它俩的不同点;

代码:

   <property  name="work"       value="seoer"/>

第一种方式直接在查询SQL语句下手,通过采用别名的方式,也就是说这种方式在得到查询结果集之前就搞定了不一致问题,获取到了跟属性名称一致的结果集;

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"">
<hibernate-mapping>
    <class name="com.b510.examples.Delivery" table="delivery" catalog="users">
        <id name="id" type="java.lang.Integer">
            <column name="id" />
            <generator class="increment" />
        </id>
        <!-- 在这里加入判别是那一种邮递方式   -->
        <discriminator column="delivery_type"></discriminator>
        
        <property name="recipient" type="java.lang.String">
            <column name="recipient" length="200" />
        </property>
        <property name="phone" type="java.lang.String">
            <column name="phone" length="200" />
        </property>
        <property name="address" type="java.lang.String">
            <column name="address" length="400" />
        </property>
        <property name="postcode" type="java.lang.String">
            <column name="postcode" length="10" />
        </property>

   <property  name="address"    value="--www.hnguotong.com--"/>

第二种方式是在获取结果集之后,对结果集下手;

  <!-- 为delivery_type  传递值       Post和Express -->
        <subclass name="com.b510.examples.PostDelivery" discriminator-value="Post">
        <property name="parcelNumber" type="java.lang.String">
            <column name="parcel_number" length="30" />
        </property>
        </subclass>
        
        
        <subclass name="com.b510.examples.ExpressDelivery" discriminator-value="Express">
        <property name="expressCompany" type="java.lang.String">
            <column name="express_company" length="40" />
        </property>
        <property name="expressNumber" type="java.lang.String">
            <column name="express_number" length="40" />
        </property>
        </subclass>
    </class>
</hibernate-mapping>

   <property  name="role"          value="find a  gust and serve for him"/>

 

HibernateTest.java

   <property  name="payment"    value="1000$"/>

代码:

</position>

/**
 *
 */
package com.b510.examples;

<!-- 小井写于2011年8月25号 -->

import org.hibernate.Session;
import org.hibernate.Transaction;

 

/**
 *
 * @author XHW
 *
 * @date 2011-7-9
 *
 */
public class HibernateTest {

 

 /**
  * @param args
  */
 public static void main(String[] args) {
  new HibernateTest().insertDelivery();
 }

 public void insertDelivery() {
  Session session = HibernateSessionFactoryUtil.getSessionFactory()
    .openSession();
  Transaction tx = session.beginTransaction();

  PostDelivery post = new PostDelivery();
  post.setRecipient("Hongten");
  post.setAddress("广州中医药大学");
  post.setPhone("119");
  post.setPostcode("510006");
  post.setparcelNumber("订单号:abc123f23");

  ExpressDelivery express = new ExpressDelivery();
  express.setRecipient("HongWei");
  express.setAddress("云南昆明");
  express.setPhone("123465");
  express.setPostcode("456789");
  express.setExpressNumber("订单号:456qwe87ewer");
  express.setExpressCompany("顺丰快递公司");
  try {
   session.save(post);
   session.save(express);
  } catch (Exception e) {
   e.printStackTrace();
   if (tx.isActive()) {
    tx.rollback();
   }
  }
  session.getTransaction().commit();
 }
}

运行效果:

log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate:
    select
        max(id)
    from
        delivery
Hibernate:
    insert
    into
        users.delivery
        (recipient, phone, address, postcode, parcel_number, delivery_type, id)
    values
        (?, ?, ?, ?, ?, 'Post', ?)
Hibernate:
    insert
    into
        users.delivery
        (recipient, phone, address, postcode, express_company, express_number, delivery_type, id)

    values
        (?, ?, ?, ?, ?, ?, 'Express', ?)

本文由10bet手机官网发布于面向对象,转载请注明出处:Java框架整合,MyBatis解决Java实体类和数据库表字段不一致方法总结

上一篇:转自大楚网,微软TTS语音引擎编程入门 下一篇:没有了
猜你喜欢
热门排行
精彩图文