MyBatis入门的详细应用实例
目录
- MyBatis
- 第一章:代理Dao方式的CRUD操作
- 1. 代理Dao方式的增删改查
- 第二章:MyBatis参数详解
- 1. parameterType
- 2. resultType
- 第三章:SqlMapConfig.xml配置文件
- 1. 定义properties标签的方式管理数据库的信息
- 2. 类型别名定义
MyBatis
第一章:代理Dao方式的CRUD操作
1. 代理Dao方式的增删改查
-
创建项目
-
UserMapper接口代码
findAll
方法用于获取所有用户的信息,将以List<User>
形式返回。findById
是根据用户的唯一标识(userId)查找用户信息,返回单个User
对象。insert
方法接收一个User
对象作为参数,将其存储到数据库中。update
方法用于更新用户信息,接收一个User
对象,通过对象的属性值更新数据库中对应记录。delete
方法依据用户 ID 删除相应记录,接收一个Integer
类型的用户 ID。findByName
可根据用户名进行查找,以字符串形式接收用户名,结果以List<User>
形式返回,适用于模糊查询或精确查询。findByCount
方法计算用户的总数,结果以Integer
类型返回。package cn.tx.mapper;import java.util.List;import cn.tx.domain.User;public interface UserMapper {public List<User> findAll();public User findById(Integer userId);public void insert(User user);public void update(User user);public void delete(Integer userId);public List<User> findByName(String username);public Integer findByCount();}
-
UserMapper.xml的配置文件代码
元素的 namespace 属性绑定到对应的 UserMapper 接口,确保 XML 中的操作与接口方法关联。
元素中的 findAll 操作,使用 select * from user 语句查询所有用户,结果将映射为 com.qcbyjy.domain.User 类型的对象。
findById 的 select 操作通过 #{id} 占位符接收 findById 方法传入的用户 ID,查询结果映射为 com.qcbyjy.domain.User 类型,参数为 int 类型。
insert 操作除了插入数据的 SQL 语句外,使用 元素在插入操作后执行,通过 select last_insert_id() 获取新插入记录的主键值,keyProperty 指明将主键值存储在 User 对象的 id 属性中,order=“AFTER” 表示在插入之后执行,resultType 为 Integer。
update 操作使用 #{} 占位符接收 update 方法传入的 User 对象的属性,更新用户表中相应记录。
delete 操作使用 #{id} 接收 delete 方法传入的用户 ID,删除相应记录。
findByName 操作的 select 语句使用 like ‘%${value}%’ 进行模糊查询,其中 ${value} 接收 findByName 方法传入的用户名,存在 SQL 注入风险,不推荐使用。
findByCount 操作通过 select count(*) from user 统计用户表中的记录数,结果为 int 类型。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.qcbyjy.mapper.UserMapper"><select id="findAll" resultType="com.qcbyjy.domain.User">select * from user</select><!-- 通过id查询 SQL语句使用#{占位符的名称,名称可以任意},仅限于基本数据类型和String类型--><select id="findById" resultType="com.qcbyjy.domain.User" parameterType="int">select * from user where id = #{id};</select><!--保存操作--><insert id="insert" parameterType="com.qcbyjy.domain.User">/*keyProperty表示要返回的属性名称order取值AFTER表示插入数据后的行为resultType表示返回值的类型*/<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">select last_insert_id();</selectKey>insert into user (username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})</insert><!-- 修改 --><update id="update" parameterType="com.qcbyjy.domain.User">update user set username = #{username},birthday = #{birthday},sex = #{sex},address=#{address} where id = #{id}</update><!-- 删除 --><delete id="delete" parameterType="Integer">delete from user where id = #{id}</delete><!-- 模糊查询 --><select id="findByName" resultType="com.qcbyjy.domain.User" parameterType="string"><!-- 第一种方式的SQL语句 select * from user where username like #{username}--><!-- 第二章SQL语句的编写 强调:'%${value}%'不能修改,固定写法(不推荐使用) -->select * from user where username like '%${value}%'</select><!-- 具体函数的查询 --><select id="findByCount" resultType="int">select count(*) from user</select></mapper>
-
UserTest的代码
- 在
init
方法中,首先使用Resources.getResourceAsStream("SqlMapConfig.xml")
从类路径加载配置文件,然后使用SqlSessionFactoryBuilder
创建SqlSessionFactory
,进而创建SqlSession
,最终通过session.getMapper(UserMapper.class)
获取UserMapper
的代理对象。 - 在
@Before
注解的init
方法中进行资源的初始化,在@After
注解的destory
方法中关闭输入流和SqlSession
,确保资源的正确管理。 testFindAll
测试方法调用mapper.findAll()
方法查询所有用户,将结果存储在List<User>
中并遍历打印。testFindById
测试方法调用mapper.findById(41)
查找 ID 为 41 的用户并打印。testInsert
测试方法创建一个新的User
对象,设置属性,调用mapper.insert(user)
插入用户,使用session.commit()
提交事务,并打印新插入用户的id
。testUpdate
测试方法先查找用户,修改用户信息,调用mapper.update(user)
更新用户信息,再提交事务。testDelete
测试方法调用mapper.delete(48)
删除 ID 为 48 的用户,提交事务。testFindByName
测试方法有两种方式,第一种传入%王%
进行模糊查询,第二种只传入王
,根据配置自动添加通配符进行模糊查询,都将结果存储在List<User>
中并遍历打印。testFindByCount
测试方法调用mapper.findByCount()
获取用户数量并打印。
package cn.tx.test;import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.List;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 org.junit.After; import org.junit.Before; import org.junit.Test;import cn.tx.domain.User; import cn.tx.mapper.UserMapper;public class UserTest {private InputStream in;private SqlSession session;private UserMapper mapper;@Beforepublic void init() throws Exception {// 加载配置文件in = Resources.getResourceAsStream("SqlMapConfig.xml");// 创建工厂对象SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);// 创建Session对象session = factory.openSession();// 获取到代理对象mapper = session.getMapper(UserMapper.class);}@Afterpublic void destory() throws IOException {in.close();session.close();}/*** 测试查询所有的方法* @throws Exception */@Testpublic void testFindAll() throws Exception {List<User> list = mapper.findAll();// 遍历for (User user : list) {System.out.println(user);}in.close();}@Testpublic void testFindById() throws Exception {User user = mapper.findById(41);System.out.println(user);in.close();}@Testpublic void testInsert() throws Exception {User user = new User();user.setUsername("美美");user.setBirthday(new Date());user.setSex("男");user.setAddress("顺义");mapper.insert(user);session.commit();System.out.println(user.getId());}@Testpublic void testUpdate() throws Exception {User user = mapper.findById(41);user.setUsername("小凤");mapper.update(user);session.commit();}@Testpublic void testDelete() throws Exception {mapper.delete(48);session.commit();}// 第一种@Testpublic void testFindByName() throws Exception {List<User> list = mapper.findByName("%王%");for (User user : list) {System.out.println(user);}}// 第二种@Testpublic void testFindByName() throws Exception {List<User> list = mapper.findByName("王");for (User user : list) {System.out.println(user);}}@Testpublic void testFindByCount() throws Exception {Integer count = mapper.findByCount();System.out.println("总记录数:"+count);}}
- 在
-
模糊查询符号使用的区别
通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。通过$可以将传入的内容拼接在中且不进行类型转换,${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。
第二章:MyBatis参数详解
1. parameterType
-
简单数据类型
int double类型 String类型 long
简单的写法:java.lang.Integer --> int integer Int Integer 都可以,框架提供简写的方式。
-
POJO(JavaBean实体类)对象类型,默认是不能简写,可以配置。
User对象
-
POJO包装对象类型
包含更多的实体类
package cn.tx.domain;import java.io.Serializable;/*** * * * **/ public class QueryVo implements Serializable {// 自己属性private String name;// user属性private User user;// role属性private Role role;public String getName() {return name;}public void setName(String name) {this.name = name;}public User getUser() {return user;}public void setUser(User user) {this.user = user;}public Role getRole() {return role;}public void setRole(Role role) {this.role = role;} }// 测试包装类查询 public List<User> findByVo(QueryVo vo);<!--包装类测试查询--> <select id="findByVo" parameterType="com.qcbyjy.domain.QueryVo" resultType="com.qcbyjy.domain.User">select * from user where username = #{user.username} </select>
2. resultType
-
返回简单数据类型
int double long String
-
返回POJO数据类型
返回User对象类型
-
resultMap结果类型
resultType可以指定pojo将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。 如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系 ,resultMap实质上还需要将查询结果映射到pojo对象中。 resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
<!--演示resultMap配置--> <select id="findUsers" resultMap="userMap">select id _id,username _username,birthday _birthday,sex _sex,address _address from user </select><!--配置resultMap,用来进行数据封装id="唯一的名称,用来被引用的"type="进行封装数据的类型"--> <resultMap id="userMap" type="com.qcbyjy.domain.User"><!--property="JavaBean中的属性"column="表中的字段"--><result property="id" column="_id"/><result property="username" column="_username" /><result property="birthday" column="_birthday" /><result property="sex" column="_sex" /><result property="address" column="_address" /> </resultMap>
第三章:SqlMapConfig.xml配置文件
1. 定义properties标签的方式管理数据库的信息
-
把数据库的信息定义property标签中的方式,在
properties
元素内直接定义jdbc
相关属性,如jdbc.driver
、jdbc.url
等,这些属性在dataSource
元素中通过${}
占位符使用,实现了配置信息的集中管理。<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><properties><property name="jdbc.driver" value="com.mysql.jdbc.Driver"/><property name="jdbc.url" value="jdbc:mysql:///mybatis_db"/><property name="jdbc.username" value="root"/><property name="jdbc.password" value="root"/></properties><!-- 配置环境们 --><environments default="mysql"><!-- 配置具体的环境 --><environment id="mysql"><!-- 配置事务管理类型 --><transactionManager type="JDBC"/><!-- 配置是否需要使用连接池,POOLED使用,UNPOOLED不使用 --><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments><!-- 加载映射的配置文件 --><mappers><mapper resource="mappers/UserMapper.xml"/></mappers> </configuration>
-
在项目中定义jdbc.properties属性文件,存储数据库相关的信息,统一管理,在
SqlMapConfig.xml
中通过<properties resource="jdbc.properties"></properties>
引入,使配置信息更易于维护。-
jdbc.properties属性文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql:///mybatis_db jdbc.username=root jdbc.password=root
-
SqlMapConfig.xml的配置文件
在 environments 元素中设置事务管理类型和数据源类型,数据源类型可选择使用连接池(POOLED)或不使用(UNPOOLED)。
元素加载映射文件,将 UserMapper.xml 引入。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><!-- <properties resource="jdbc.properties"></properties>读取外部的配置文件resource="文件的相对路径写法"。例如:jdbc.properties 或者 cn/tx/xxx/jdbc.properties--><properties resource="jdbc.properties"></properties><!-- 配置环境们 --><environments default="mysql"><!-- 配置具体的环境 --><environment id="mysql"><!-- 配置事务管理类型 --><transactionManager type="JDBC"/><!-- 配置是否需要使用连接池,POOLED使用,UNPOOLED不使用 --><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments><!-- 加载映射的配置文件 --><mappers><mapper resource="mappers/UserMapper.xml"/></mappers> </configuration>
2. 类型别名定义
-
MyBatis自已有类型别名的注册类,咱们编写int或者integer通过注册可以找到java.lang.Integer
-
咱们自己也可以进行别名的注册
-
SqlMapConfig.xml的配置文件
<!-- 定义别名 --> <typeAliases><!-- 把com.qcbyjy.domain.User使用user别名来显示,别名user User USER都可以,默认是忽略大写的 <typeAlias type="com.qcbyjy.domain.User" alias="user"/>--><!-- 针对com.qcbyjy.domain包下的所有的类,都可以使用当前的类名做为别名 --><package name="com.qcbyjy.domain"/> </typeAliases>
-
UserMapper.xml的配置文件使用别名
<!-- resultType="com.qcbyjy.domain.User" 原来是全路径resultType="user" 现在使用的是别名的方式--> <select id="findAll" resultType="user">select * from user </select>
-
-
相关文章:

MyBatis入门的详细应用实例
目录 MyBatis第一章:代理Dao方式的CRUD操作1. 代理Dao方式的增删改查 第二章:MyBatis参数详解1. parameterType2. resultType 第三章:SqlMapConfig.xml配置文件1. 定义properties标签的方式管理数据库的信息2. 类型别名定义 MyBatis 第一章&…...

Sequelize ORM sql 语句工具
Sequelize ORM sql 语句工具 初始化配置 Sequelize orm 配置文章落日沉溺于海 在命令行中全局安装 npm i -g sequelize-clisequelize 执行需要匹配 mysql2 对应的依赖(安装 mysql2) npm i sequelize mysql2初始化项目 sequelize init熟悉初始化项目后…...

增强LabVIEW与PLC通信稳定性
在工业自动化系统中,上位机与PLC之间的通信稳定性至关重要,尤其是在数据采集和控制任务的实时性要求较高的场景中。LabVIEW作为常用的上位机开发平台,通过合理优化通信协议、硬件接口、数据传输方式以及系统容错机制,可以大大提升…...

UDP系统控制器_音量控制、电脑关机、文件打开、PPT演示、任务栏自动隐藏
UDP系统控制器(ShuiYX) 帮助文档 概述 本程序设计用于通过UDP协议接收指令来远程控制计算机的音量、执行特定命令和其他功能。为了确保程序正常工作,请确认防火墙和网络设置允许UDP通信,并且程序启动后会最小化到托盘图标。 命令格式及说明 音量控制…...

NK细胞杀伐功能如何实现?
在人体的免疫系统中,自然杀伐细胞(Natural Killer Cells,简称NK细胞)是一类完全自然的免疫激活力量。它们为人体提供了快速反应能力,不依赖类元的特定识别力,但能直接寻找和毁灭毒病感染细胞和肿瘤细胞。那…...

Ubuntu搭建ES8集群+加密通讯+https访问
目录 写在前面 一、前期准备 1. 创建用户和用户组 2. 修改limits.conf文件 3. 关闭操作系统swap功能 4. 调整mmap上限 二、安装ES 1.下载ES 2.配置集群间安全访问证书密钥 3.配置elasticsearch.yml 4.修改jvm.options 5.启动ES服务 6.修改密码 7.启用外部ht…...

PC寄存器(Program Counter Register)jvm
在JVM(Java虚拟机)中,PC寄存器(Program Counter Register)扮演着至关重要的角色。以下是对JVM中PC寄存器的详细解释: 一、定义与功能 定义: JVM中的PC寄存器,也被称为程序计数器,是对物理PC寄存器的一种抽象模拟。它用于存储当前线程所执行的字节码指令的地址,即指…...

预览和下载 (pc和微信小程序)
1.微信小程序 预览pdf 或者 图片等 //utils.js 文件//通过接口返回文件链接 打开文档 export default function previewFile({ downLinkUrl, tempFilePath }) {let url "https://" downLinkUrl.replace("http://", "").replace("https:…...

使用 DeepSpeed 微调 OPT 基础语言模型
文章目录 OPT 基础语言模型Using OPT with DeepSpeedmain.py 解析1、导入库和模块2、解析命令行参数3、main 函数3.1 设备与分布式初始化3.2 模型与数据准备3.3 定义评估函数3.4 优化器与学习率调度器设置3.5 使用 deepspeed 进行模型等初始化3.6 训练循环3.7 模型保存 4、dsch…...

BSM和BMS什么区别?
BSM BSM(Battery System Manager)是指用于管理和控制电动车辆的电池系统的设备,其功能包括监测电池状态、控制充放电过程、保护电池安全等。 BMS BMS(Battery Management System)是指用于监测、控制和保护电池组的设…...

使用Maven打包javaagent.jar
1、简介 javaagent 是 Java1.5 之后引入的新特性,其主要作用是在class被加载之前对其拦截,以插入我们的字节码。 java1.5 之前使用的是JVMTI(jvm tool interface)技术来实现对class的拦截,不过这个是用 C 编写的&#…...

R语言混合模型回归GBTM群组轨迹模型绘图可视化研究
全文链接:https://tecdat.cn/?p38581 在回归分析的广袤领域中,面对具有多条未知函数线的复杂数据时,传统方法常常捉襟见肘。混合模型作为一种强有力的分析手段应运而生,其在处理此类复杂情境时展现出独特的优势与潜力(…...

【毕业设计】A079-基于Java的影院订票系统的设计与实现
🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看项目链接获取⬇️,记得注明来意哦~🌹 赠送计算机毕业设计600个选题ex…...

Vue.js前端框架教程11:Vue监听器watch和watchEffect
文章目录 监听器(watchers)基本用法deep: trueimmediate: true总结 watchEffect基本用法自动追踪依赖停止监听与 watch 的对比性能优化总结 监听器(watchers) 在 Vue 中,监听器(watchers)是一种…...

疾风大模型气象系统:精准预报,引领未来
精准预报,引领未来 在当今快速变化的世界中,天气预报已成为日常生活和社会运行中不可或缺的一部分。从规划日常出行到防范极端天气影响,高精准的气象服务正在重新定义我们的生活方式。而在这一领域,疾风大模型气象系统以其卓越的技术实力和领先的预测能力,正引领气象服务…...

U9应收单拉单生成时找不到退货单
财务说做应收单时抓不到一张退货单。2022年单据。这样的单据让人联想翩翩,胡思乱想。怎么复杂怎么想,钻了牛角尖。分析了1天也没有结果。不知道系统的逻辑,只能用猜想的形式去分析。 问过顾问之后,原来是单据类型错了。从而知道了…...

设计模式--单例模式【创建型模式】
设计模式的分类 我们都知道有 23 种设计模式,这 23 种设计模式可分为如下三类: 创建型模式(5 种):单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。结构型模式(7 种)࿱…...

挑战一个月基本掌握C++(第七天)了解指针,引用,时间,输入输出,结构体,vector容器,数据结构 - 通用完结
一 指针 每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。 下面的实例,它将输出定义的变量地址: #include <iostream>using…...

百度面试手撕 go context channel部分学习
题目 手撕 对无序的切片查询指定数 使用context进行子协程的销毁 并且进行超时处理。 全局变量定义 var (startLoc int64(0) // --- 未处理切片数据起始位置endLoc int64(0) // --- 切片数据右边界 避免越界offset int64(0) // --- 根据切片和协程数量 在主线程 动态设…...

Spring事务管理详解
一、什么是事务管理 事务是一个最小的不可再分的工作单元。 一个事务对应一套完整的业务操作。事务管理是指这些操作要么全部成功执行,要么全部回滚,从而保证数据的一致性和完整性。比如银行转账,需要保证转出和转入是一个原子操作。Spring提…...

社区版 IDEA 开发webapp 配置tomcat
1.安装tomcat 参考Tomcat配置_tomcat怎么配置成功-CSDN博客 2.构建webapp项目结构 新建一个普通项目 然后添加webapp的目录结构: main目录下新建 webapp 文件夹 webapp文件夹下新建WEB_INF文件夹 *WEB_INF目录下新建web.xml wenapp文件夹下再新建index.html …...

打 印 菱 形
本题要求你写个程序打印成菱形的形状。例如给定17个符号,要求按下列格式打印 **** *********所谓“菱形形状”,是指每行输出奇数个符号;各行符号中心对齐;相邻两行符号数差2;符号数从1开始先从小到大顺序递增ÿ…...

ffmpeg翻页转场动效的安装及使用
文章目录 前言一、背景二、选型分析2.1 ffmpeg自带的xfade滤镜2.2 ffmpeg使用GL Transition库2.3 xfade-easing项目 三、安装3.1、安装依赖([参考](https://trac.ffmpeg.org/wiki/CompilationGuide/macOS#InstallingdependencieswithHomebrew))3.2、获取…...

[RocketMQ] 发送重试机制与消费重试机制~
发送重试 RocketMQ 客户端发送消息时,由于网络故障等因素导致消息发送失败,这时客户端SDK会触发重试机制,尝试重新发送以达到调用成功的效果。 触发条件 客户端消息发送请求失败或超时。服务端节点处于重启或下线状态。服务端运行慢造成请…...

基于Redis的网关鉴权方案与性能优化
文章目录 前言一、微服务鉴权1.1 前端权限检查1.2 后端权限检查1.3 优缺点 二、网关鉴权2.1 接口权限存储至Redis2.2 网关鉴权做匹配 总结 前言 在微服务架构中,如何通过网关鉴权结合Redis缓存提升权限控制的效率与性能。首先,文章对比了两种常见的权限…...

计算机网络-L2TP VPN基础概念与原理
一、概述 前面学习了GRE和IPSec VPN,今天继续学习另外一个也很常见的VPN类型-L2TP VPN。 L2TP(Layer 2 Tunneling Protocol) 协议结合了L2F协议和PPTP协议的优点,是IETF有关二层隧道协议的工业标准。L2TP是虚拟私有拨号网VPDN&…...

Node.js day-01
01.Node.js 讲解 什么是 Node.js,有什么用,为何能独立执行 JS 代码,演示安装和执行 JS 文件内代码 Node.js 是一个独立的 JavaScript 运行环境,能独立执行 JS 代码,因为这个特点,它可以用来编写服务器后端…...

vue el-dialog实现可拖拉
el-dialog实现拖拉,每次点击度居中显示,以下贴出代码具体实现,我是可以正常拖拉并且每次度显示在中间,效果还可以,需要的可以丢上去跑跑 组件部分: <el-dialog:visible.sync"dialogVisible"…...

go配置文件
https://github.com/spf13/viper viper golang中常用的配置文件工具为viper库,是一个第三方库。viper功能: 解析JSON、TOML、YAML、HCL等格式的配置文件。监听配置文件的变化(WatchConfig),不需要重启程序就可以读到最新的值。...

C++ OpenGL学习笔记(2、绘制橙色三角形绘制、绿色随时间变化的三角形绘制)
相关文章链接 C OpenGL学习笔记(1、Hello World空窗口程序) 目录 绘制橙色三角形绘制1、主要修改内容有:1.1、在主程序的基础上增加如下3个函数1.2、另外在主程序外面新增3个全局变量1.3、编写两个shader程序文件 2、initModel()函数3、initS…...