MyBatis

主要内容

  • Mybatis 入门
  • Mybatis 的基本使用 J
  • ava 日志处理框架
  • Mybatis 配置完善
  • SqlSession 常用 API
  • Mapper 动态代理
  • 动态 SQL
  • Mybatis 缓存
  • Mybatis 多表关联查询
  • Mybatis 注解的使用
  • Mybatis Generator 工具的使用
  • PageHelper 分页插件
  • Mybatis 与 Servlet 整合

一、MyBatis入门

1 什么是框架

框架(Framework)是一个框子——指其约束性,也是一个架子——指其支撑性。是一个基本概念上的结构,用于去解决或者处理复杂的问题。框架这个广泛的定义使用的十分流行,尤其在软件概念。

框架( Framework )是构成一类特定软件可复用设计的一组相互协作的类。框架规定了 你的应用的体系结构。它定义了整体结构,类和对象的分割,各部分的主要责任,类和对象 怎么协作,以及控制流程。框架预定义了这些设计参数,以便于应用设计者或实现者能集中 精力于应用本身的特定细节。

如果将开发完成的软件比作是一套已经装修完毕的新房,那框架就好比是一套已经修建 好的毛坯房。用户直接购买毛坯房,建筑质量和户型合理有保证,还省去了自己建造房屋的 时间,一举多得。

在开发过程是使用框架,同样可以保证减少开发时间、降低开发难度,并且还保证设计质量。好比和世界上最优秀的软件工程师是一个项目的,并且他们完成的还是基础、全局的 工作。想想是不是很嗨的一件事情。


框架还有一个作用是约束。莎士比亚说,“一千个观众眼中有一千个哈姆雷特” 即仁者见 仁,智者见智.说每个人都会对作品有不同的理解,每个人对待任何事物都有自己的看法,一 千个人就有可能有一千种不同的看法 1000 人心中有 1000 个哈姆雷特。同样的技术解决同 样的问题会产生不同流程和风格的解决方案,而采用一种框架其实就是限制用户必须使用其 规定的方案来实现,可以降低程序员之间沟通以及日后维护的成本。

总之,框架是一个半成品,已经对基础的代码进行了封装并提供相应的 API,开发者在 使用框架是直接调用封装好的 API 可以省去很多代码编写,从而提高工作效率和开发速度。

使用框架可以带来的好处:

  • 重用代码大大增加,软件生产效率和质量也得到了提高;
  • 代码结构的规范化,降低程序员之间沟通以及日后维护的成本;
  • 知识的积累,可以让那些经验丰富的人员去设计框架和领域构件,而不必限于低层编程;
  • 软件设计人员要专注于对领域的了解,使需求分析更充分;
  • 允许采用快速原型技术; 有利于在一个项目内多人协同工作;
  • 大粒度的重用使得平均开发费用降低,开发速度加快,开发人员减少,维护费用降低, 而参数化框架使得适应性、灵活性增强。

2 什么是 ORM

ORM,Object-Relationl Mapping,对象关系映射,它的作用是在关系型数据库和对 象之间作一个映射处理。

JDBC 的缺点:需要手动的完成面向对象的 Java 语言、面向关系的数据库之间数据的 转换,代码繁琐无技术含量,影响了开发效率。

如图所示,查询是需要手动的将结果集的列数据转换为 Java 对象的属性;而添加操作 时需要手动将 Java 对象的属性转换为数据库表的列字段。

关于面向对象的 Java 语言、面向关系的数据库之间数据的转换必须要做,问题在于这个转换是否可以不由开发者来做?答案是可以的。ORM 框架就是专门来解决这个问题的, 相当于在面向对象语言和关系数据库之间搭建一个桥梁。这样我们在具体的操作数据库的时 候,只要像平时操作对象一样操作它就可以了,ORM 框架会根据映射完成对数据库的操作, 就不需要再去和复杂的 SQL 语句打交道了。

3 MyBatis 简介

MyBatis 本是 Apache 的一个开源项目 iBatis, 2010 年这个项目由 Apache Software Foundation 迁移到了 Google Code,且改名为 MyBatis 。2013 年 11 月迁移到 GitHub。

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。 MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使 用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java 对象)映射成数据库中的记录。

简单的说:MyBatis 是一个半自动 ORM 框架,其本质是对 JDBC 的封装。使用 MyBatis 重点需要程序员编写 SQL 命令,不需要写一行 JDBC 代码。

与 Hibernate 的比较
Hibernate 是一个全自动的 ORM 框架。因为 Hibernate 创建了 Java 对象和数据库表 之间的完整映射,可以完全以面向对象的思想来操作数据库,程序员不需要手写 SQL 语句, 而 MyBatis 中还需要手写 SQL 语句,所以是半自动化的,工作量要大于 Hibernate。

为什么半自动化的 Mybatis 比自动化的 Hibernate 受欢迎?
MyBatis 需要手写 SQL 语句,所以工作量要大于Hibernate。但是也正是由于自定义 SQL 语句,所以其灵活性、可优化性就超过了 Hibernate。

Hibernate 封装了 SQL 语句,由开发者对对象操作,Hibernate 来生成 SQL 语句。虽 然也可以通过映射配置来控制生成的 SQL 语句,但是对于要生成复杂的 SQL 语句,很难实 现,或者实现后导致性能的丢失。

而 MyBatis 将手写 SQL 语句的工作丢给开发者,可以更加精确的定义 SQL,更加灵活, 也便于优化性能。完成同样功能的两条 SQL 语句的性能可能相差十几倍到几十倍,在高并 发、快响应要求下的互联网系统中,对性能的影响更明显。

MyBatis 对存储过程可提供很好的支持。另外 MyBatis 的开发工作量大不意味着学习 成本大。对于新手,学习 Hibernate 时间成本比 Mybatis 大很多,Mybatis 很快就上手了。

总之,因为 MyBatis 具有封装少、映射多样化、支持存储过程、可以进行 SQL 语句优 化等特点,符合互联网高并发、大数据、高性能、高响应的要求,使它取代 Hibernate 成 为了 Java 互联网中首选的持久框架。而对于对性能要求不高的比如内部管理系统、ERP 等 可以使用 Hibernate。

二、 Mybatis 的基本使用

1 Mybatis 的 jar 包下载地址

https://github.com/mybatis/mybatis-3/releases

2 Mybatis 的 jar 包介绍


3 核心 API 介绍

3.1核心 API

  • SqlSessionFactoryBuilder
    SqlSessionFactoryBuilder 的作用是使用构建者模式创建 SqlSessionFactory 接口 对象。

  • SqlSessionFactory
    SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。

  • SqlSession
    如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数 据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 方法提交或者回滚事务。

  • Mapper
    映射器。由一个 Java 接口和 XML 文件(或者注解构成),需要给出对应的 SQL 和映 射规则,负责发送 SQL 去执行并返回结果。

3.2核心 API 工作流程

3.3生命周期

  • SqlSessionFactoryBuilder:
    该类用来创建 SqlSessionFactory 对象,当 SqlSessionFactory 对象被创建后, SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的 方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法 作用域。
  • SqlSessionFactory:
    SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。由于 SqlSessionFactory 是 一 个 对 数 据 库 的 连 接 池 , 所 以 它 占 据 着 数 据 库 的 连 接 资 源 。 如 果 创 建 多 个SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也 会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。因 此 SqlSessionFactory 是一个单例,让它在应用中被共享。
  • SqlSession:
    SqlSession 应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让 它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以 用 try…catch…finally… 语句来保证其正确关闭。 所以 SqlSession 的最佳的作用域是请求或方法作用域。
  • Mapper:
    由于 SqlSession 的关闭,它的数据库连接资源也会消失,所以它的生命周期应该小 于等于 SqlSession 的生命周期。Mapper 代表的是一个请求中的业务处理,所以它应该 在一个请求中,一旦处理完了相关的业务,就应该废弃它。

4 Mybatis 的配置文件

https://mybatis.org/mybatis-3/zh/configuration.html
在 Mybatis 中配置文件有两种:

  • 全局配置文件
  • 映射配置文件

4.1全局配置文件

全局配置文件的名称是自定义的,在 JavaProject 项目中需要放到 src 目录下。
全局配置文件的作用是完成一些全局性的配置,如:对 Mybatis 框架的设置、别名设置、 环境设置、指定映射配置文件等相关配置。

<?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> </configuration>

4.1.1 properties 标签

Mybatis 配置文件中的属性定义。properties 标签中允许内部定义属性,也可以是外部的 properties 文件定义属性。无论是内部定义还是外部定义,都可以使用${name}获取值。
配置文件中内部定义

<properties> <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/> <property name="jdbc.url" value="jdbc:mysql://localhost:3306/bjsxt"/> <property name="jdbc.username" value="root"/> <property name="jdbc.password" value="root"/> </properties>

配置文件中外部定义

<properties resource="db.properties"></properties>

4.1.2 settings 标签

setting 标签的配置是配置 MyBatis 框架运行时的一些行为的,例如缓存、延迟加载、 结果集控制、执行器、分页设置、命名规则等一系列控制性参数,其所有的 setting 配置都 放在父标签 settings 标签中。

<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/><setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25"/> <setting name="defaultFetchSize" value="100"/> <setting name="safeRowBoundsEnabled" value="false"/> <setting name="mapUnderscoreToCamelCase" value="false"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="OTHER"/> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>


4.1.3 typeAliases 标签

类型别名可为 Java 类型设置一个缩写名字。

<typeAliases> <typeAlias alias="user" type="com.bjsxt.pojo.User" />
</typeAliases>

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean

<typeAliases> <package name="com.bjsxt.pojo"/>
</typeAliases>

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的

4.1.4 environments 标签

MyBatis 可以配置多个环境。这可以帮助你 SQL 映射对应多种数据库等。比如说,想为 开发、测试、发布产品配置不同的环境。

<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <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><transactionManager type="JDBC"/> <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>
4.1.4.1 transactionManager 节点

事务处理器。

在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]")

JDBC :这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来 管理事务作用域。

MANAGED :不做事务处理。

4.1.4.2 dataSource 标签
  • dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
  • UNPOOLED:使用直连。
  • POOLED:使用池连。
  • JNDI :使用 JNDI 方式连接

4.1.5 mapper 标签

指定映射配置文件
使用相对于类路径指定映射配置文件

<mappers> <mapper resource="com/bjsxt/mapper/UserMapper.xml"/>
</mappers>

使用 filter:///协议指定映射配置文件

<mappers> <mapper url="file:///D:\code\mybatis\src\com\bjsxt\mapper\UserMapper.xml" />
</mappers>

指定映射接口

<mappers> <mapper class="com.bjsxt.mapper.UserMapper"/>
</mappers>

通过包名指定映射接口

<mappers> <package name="com.bjsxt.mapper"/>
</mappers>

4.2映射配置文件

映射配置文件主要是用来编写 sql 语句的,结果集的映射关系的指定,以及缓存的一些 配置等等。

<?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.bjsxt.mapper.UserMapper"> </mapper>

4.2.1 resultMap 标签

指定查询结果集与对象的映射关系,是最复杂也是最强大的标签。
id:唯一标识。
type:类的完全名, 或者一个类型别名

<resultMap id="userMapper" type="com.bjsxt.pojo.User"> <id property="userid" column="user_id"/> <result property="username" column="user_name"/>
</resultMap>
4.2.1.1 id 标签

指定主键中的值,用于标识一个结果映射。

4.2.1.2 result 标签

指定非主键中的值,用于标识一个结果映射。

4.2.1.3 association 标签

通常用来映射一对一的关系。

4.2.1.4 collection 标签

通常用来映射一对多的关系。

4.2.2 select 标签

查询语句。

  • id:当前查询语句的唯一标识,该属性的值不能重复。
  • parameterType:指定参数类型。该属性是可选属性。因为 MyBatis 可以通过类型处理 器(TypeHandler)推断出具体传入语句的参数。
  • resultType:期望从这条语句中返回结果的类全名或别名。
  • resultMap:使用 resultMap 标签来处理结果集映射。
<select id="selectUser" parameterType="int" resultType="u"> select * from users where userid = #{userid}
</select>
4.2.3 insert 标签

添加语句。

  • id:当前添加语句的唯一标识,该属性的值不能重复。
  • parameterType:指定参数类型,可以给定别名。该属性是可选属性。
<insert id="insertUser" parameterType="com.bjsxt.pojo.User">insert into users values(default ,#{username},#{usersex})
</insert>
4.2.4 update 标签

更新语句。

  • id:当前更新语句的唯一标识,该属性的值不能重复。
  • parameterType:指定参数类型,可以给定别名。该属性是可选属性。
<update id="updateUser" parameterType="com.bjsxt.pojo.User"> update users set username=#{username},usersex=#{usersex} where userid =#{userid}
</update>
4.2.5 delete 标签 删除语句。
  • id:当前删除语句的唯一标识,该属性的值不能重复。
  • parameterType:指定参数类型,可以给定别名。该属性是可选属性。
<delete id="deleteUser" parameterType="int"> delete from users where userid = #{userid}
</delete>
4.2.6 sql 标签

可以用来定义可重用的 SQL 代码片段。通过标签引入该片段。

  • id:当前 SQL 片段的唯一标识,该属性的值不能重复。
<sql id="userColumns"> userid,username,usersex
</sql>
<select id="selectUser" parameterType="int" resultType="u"> select <include refid="userColumns"/> from users
</select>

5 Mybatis 的入门案例

5.1搭建环境

5.1.1 创建表

CREATE TABLE `users` ( `userid` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(20) DEFAULT NULL, `usersex` varchar(10) DEFAULT NULL, PRIMARY KEY (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

5.1.2 添加 DTD 约束文件

在没有联网的情况下,如果让 dtd 约束继续起作用,并且出现标签提示,可以通过引入 本地 dtd 文件来实现。
下 载 dtd : 在 浏 览 器 中 输 入 dtd 的 网 络 地 址 即 可 实 现 下 载 。 比 如 : http://mybatis.org/dtd/mybatis-3-config.dtd
将下载的 dtd 拷贝到本地的一个目录下
Idea 操作路径:File—Settings—Languages & Frameworks

其中 URI 复制 dtd 的网络地址即可。File 选择 dtd 文件在本地的地址。OK!

【注】:在 MyBatis 的核心 jar 包中就提供了 mybatis-3-config.dtd

5.1.3 创建项目

5.1.4 添加 jar 包


5.1.5 创建实体

package com.bjsxt.pojo;public class Users {private int userid;private String username;private String usersex;public int getUserid() {return userid;}public void setUserid(int userid) {this.userid = userid;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getUsersex() {return usersex;}public void setUsersex(String usersex) {this.usersex = usersex;}
}

5.1.6 创建 properties 文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/bjsxt
jdbc.username=root
jdbc.password=root

5.1.7 创建全局配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--引入properties文件--><properties resource="db.properties"/><!-- 开启SLF4J做日志处理--><settings><setting name="logImpl" value="SLF4J"/></settings><!--配置别名--><typeAliases><typeAlias type="com.bjsxt.pojo.Users" alias="u"/><package name="com.bjsxt.pojo"/></typeAliases><!--环境的配置--><environments default="development"><environment id="development"><!-- 配置事务--><transactionManager type="JDBC"></transactionManager><!-- 配置数据源--><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="com/bjsxt/mapper/UsersMapper.xml"/></mappers></configuration>

5.1.8 创建映射配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UserMapper"></mapper>

5.2查询数据

5.2.1 修改映射配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UserMapper"><!--查询所有用户--><select id="selectUsersAll" resultType="com.bjsxt.pojo.Users">select * from users</select></mapper>

5.2.2 创建 UsersDao 接口

package com.bjsxt.dao;import com.bjsxt.pojo.Users;import java.io.IOException;
import java.util.List;
import java.util.Map;public interface UsersDao {List<Users> selectUsersAll()throws IOException;}

5.2.3 创建 UsersDao 接口实现类

package com.bjsxt.dao.impl;import com.bjsxt.dao.UsersDao;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.cursor.Cursor;
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 java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;public class UsersDaoImpl implements UsersDao {/*** 查询所有用户* @return*/@Overridepublic List<Users> selectUsersAll()throws IOException {//创建SqlSessionFactory对象InputStream inputStream = Resources.getResourceAsStream("mybatis-cfg.xml");SqlSessionFactory sqlSessionFacotry = new SqlSessionFactoryBuilder().build(inputStream);//获取SqlSession对象SqlSession sqlSession = sqlSessionFacotry.openSession();//通过SqlSession对象下的API完成对数据库的操作List<Users> list = sqlSession.selectList("com.bjsxt.mapper.UserMapper.selectUsersAll");//关闭SqlSession对象sqlSession.close();return list;}
}

5.2.4 创建测试类

package com.bjsxt.test;import com.bjsxt.dao.UsersDao;
import com.bjsxt.dao.impl.UsersDaoImpl;
import com.bjsxt.pojo.Users;import java.io.IOException;
import java.util.List;public class Test {public static void main(String[] args)throws IOException {UsersDao usersDao = new UsersDaoImpl();List<Users> list =  usersDao.selectUsersAll();for(Users users:list){System.out.println(users.getUserid()+"\t"+users.getUsername()+"\t"+users.getUsersex());}}
}

5.3根据用户 Id 查询数据

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UserMapper"><!--查询所有用户--><select id="selectUsersAll" resultType="com.bjsxt.pojo.Users">select * from users</select><!--根据用户ID查询用户--><select id="selectUsersById" parameterType="int" resultType="users">select * from users where userid = #{suibian}</select>
</mapper>

5.3.1 修改映射配置文件

package com.bjsxt.dao;import com.bjsxt.pojo.Users;import java.io.IOException;
import java.util.List;
import java.util.Map;public interface UsersDao {List<Users> selectUsersAll()throws IOException;Users selectUsersById(int userid)throws IOException;
}

5.3.3 修改 UsersDao 接口实现类

package com.bjsxt.dao.impl;import com.bjsxt.dao.UsersDao;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.cursor.Cursor;
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 java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;public class UsersDaoImpl implements UsersDao {/*** 查询所有用户* @return*/@Overridepublic List<Users> selectUsersAll()throws IOException {//创建SqlSessionFactory对象InputStream inputStream = Resources.getResourceAsStream("mybatis-cfg.xml");SqlSessionFactory sqlSessionFacotry = new SqlSessionFactoryBuilder().build(inputStream);//获取SqlSession对象SqlSession sqlSession = sqlSessionFacotry.openSession();//通过SqlSession对象下的API完成对数据库的操作List<Users> list = sqlSession.selectList("com.bjsxt.mapper.UserMapper.selectUsersAll");//关闭SqlSession对象sqlSession.close();return list;}/***  根据用户ID查询用户* @param userid* @return* @throws IOException*/@Overridepublic Users selectUsersById(int userid) throws IOException {//创建SqlSessionFactory对象InputStream inputStream = Resources.getResourceAsStream("mybatis-cfg.xml");SqlSessionFactory sqlSessionFacotry = new SqlSessionFactoryBuilder().build(inputStream);//获取SqlSession对象SqlSession sqlSession = sqlSessionFacotry.openSession();Users users = sqlSession.selectOne("com.bjsxt.mapper.UserMapper.selectUsersById",userid);sqlSession.close();return users;}
}

5.3.4 修改测试类

package com.bjsxt.test;import com.bjsxt.dao.UsersDao;
import com.bjsxt.dao.impl.UsersDaoImpl;
import com.bjsxt.pojo.Users;import java.io.IOException;
import java.util.List;public class Test {public static void main(String[] args)throws IOException {UsersDao usersDao = new UsersDaoImpl();/*List<Users> list =  usersDao.selectUsersAll();for(Users users:list){System.out.println(users.getUserid()+"\t"+users.getUsername()+"\t"+users.getUsersex());}*/Users users = usersDao.selectUsersById(1);System.out.println(users.getUserid()+"\t"+users.getUsername()+"\t"+users.getUsersex());}
}

5.4Mybatis 中的参数绑定

在映射配置文件中向 SQL 语句中绑定参数的语法结构为#{ }和${ }
#{ } 和 ${ }的区别:
#{ } 解析为一个 JDBC 预编译语句(PreparedStatement)的参数标记符占位符 。使 用该方式可避免 SQL 注入。
${ }仅仅为一个纯碎的 String 替换,在 Mybatis 的动态 SQL 解析阶段将会进行变量替 换。${ }在预编译之前已经被变量替换了,这会存在 SQL 注入问题。

5.5创建 Mybatis 的工具类

5.5.1 ThreadLocal 介绍

ThreadLocal 提供了线程内存储变量的能力,这些变量不同之处在于每一个线程读取的 变量是对应的互相独立的。通过getset 方法就可以得到当前线程对应的值。

5.5.2 使用 ThreadLocal 存储 SqlSession

如果多个 DML 操作属于一个事务,因为 commit()和 rollback()都是由 SqlSession 完成的, 所以必须保证使用一个 SqlSession。但是多个不同的 DML 操作可能在不同类的不同方法中, 每个方法中要单独的获取 SqlSession。比如商城下订单时,其实涉及商品库存变化、订单添 加、订单明细添加、付款、日志添加等多个 DML 操作,分布在不同类中。
如何在多个 DML 操作之间使用同一个 SqlSession 呢,可以使用 ThreadLocal 来存储。保 证一个线程中的操作使用的都是一个 SqlSession。

在 Web 项目中用户的每次请求会启动一个新的线程,比如点击”结算”完成购物车结 算。在 Java 项目中每次启动 main()也会自动开启一个 main 线程

5.5.3 创建 Mybatis 工具类

package com.bjsxt.utils;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 java.io.IOException;
import java.io.InputStream;public class MybatisUtils {private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();private static SqlSessionFactory sqlSessionFactory = null;static{//创建SqlSessionFactoryInputStream is = null;try{is = Resources.getResourceAsStream("mybatis-cfg.xml");}catch (IOException e){e.printStackTrace();}sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);}//获取SqlSessionpublic static SqlSession getSqlSession(){SqlSession sqlSession = threadLocal.get();if(sqlSession == null){sqlSession = sqlSessionFactory.openSession();threadLocal.set(sqlSession);}return sqlSession;}//关闭SqlSessionpublic static void closeSqlSession(){SqlSession sqlSession = threadLocal.get();if(sqlSession != null){sqlSession.close();threadLocal.set(null);}}
}

5.6完成 DML 操作

5.6.1 Mybatis 的事务提交方式

在 Mybatis 中事务提交方式默认为手动提交,这与 JDBC 是不同的。在 JDBC 中事务默认 提交方式为自动提交。
手动提交事务(默认)

SqlSession sqlSession = sqlSessionFacotry.openSession();

自动提交事务

SqlSession sqlSession = sqlSessionFacotry.openSession(true);

5.6.2 添加用户操作

5.6.2.1 修改映射配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UserMapper"><!--查询所有用户--><select id="selectUsersAll" resultType="com.bjsxt.pojo.Users">select * from users</select><!--根据用户ID查询用户--><select id="selectUsersById" parameterType="int" resultType="users">select * from users where userid = #{suibian}</select><!--添加用户--><insert id="insertUsers" >insert into users value(default ,#{username},#{usersex})</insert>
</mapper>
5.6.2.2 修改 UsersDao 接口
package com.bjsxt.dao;import com.bjsxt.pojo.Users;import java.io.IOException;
import java.util.List;
import java.util.Map;public interface UsersDao {List<Users> selectUsersAll()throws IOException;Users selectUsersById(int userid)throws IOException;void insertUsers(Users users);
}
5.6.2.3 修改 UsersDao 接口实现类
package com.bjsxt.dao.impl;import com.bjsxt.dao.UsersDao;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.cursor.Cursor;
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 java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;public class UsersDaoImpl implements UsersDao {/*** 查询所有用户* @return*/@Overridepublic List<Users> selectUsersAll()throws IOException {//创建SqlSessionFactory对象InputStream inputStream = Resources.getResourceAsStream("mybatis-cfg.xml");SqlSessionFactory sqlSessionFacotry = new SqlSessionFactoryBuilder().build(inputStream);//获取SqlSession对象SqlSession sqlSession = sqlSessionFacotry.openSession();//通过SqlSession对象下的API完成对数据库的操作List<Users> list = sqlSession.selectList("com.bjsxt.mapper.UserMapper.selectUsersAll");//关闭SqlSession对象sqlSession.close();return list;}/***  根据用户ID查询用户* @param userid* @return* @throws IOException*/@Overridepublic Users selectUsersById(int userid) throws IOException {//创建SqlSessionFactory对象InputStream inputStream = Resources.getResourceAsStream("mybatis-cfg.xml");SqlSessionFactory sqlSessionFacotry = new SqlSessionFactoryBuilder().build(inputStream);//获取SqlSession对象SqlSession sqlSession = sqlSessionFacotry.openSession();Users users = sqlSession.selectOne("com.bjsxt.mapper.UserMapper.selectUsersById",userid);sqlSession.close();return users;}@Overridepublic void insertUsers(Users users) {SqlSession sqlSession = MybatisUtils.getSqlSession();sqlSession.insert("com.bjsxt.mapper.UserMapper.insertUsers", users);}
}
5.6.2.4 创建 UsersService 业务层接口
package com.bjsxt.service;import com.bjsxt.pojo.Users;import java.util.Map;public interface UsersService {void addUsers(Users users);
}
5.6.2.5 创建 UsersService 业务层接口实现类
package com.bjsxt.service.impl;import com.bjsxt.dao.UsersDao;
import com.bjsxt.dao.impl.UsersDaoImpl;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;import java.util.Map;public class UsersServiceImpl implements UsersService {/*** 添加用户* @param users*/@Overridepublic void addUsers(Users users) {SqlSession sqlSession = MybatisUtils.getSqlSession();try{UsersDao usersDao =  new UsersDaoImpl();usersDao.insertUsers(users);sqlSession.commit();}catch (Exception e){e.printStackTrace();sqlSession.rollback();}finally{MybatisUtils.closeSqlSession();}}
}
5.6.2.6 创建测试类
package com.bjsxt.test;import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import com.bjsxt.service.impl.UsersServiceImpl;public class AddUserTest {public static void main(String[] args) {UsersService usersService = new UsersServiceImpl();Users users = new Users();users.setUsername("zhangsan");users.setUsersex("male");usersService.addUsers(users);}
}

5.6.3 更新用户操作

5.6.3.1 修改映射配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UserMapper"><!--查询所有用户--><select id="selectUsersAll" resultType="com.bjsxt.pojo.Users">select * from users</select><!--根据用户ID查询用户--><select id="selectUsersById" parameterType="int" resultType="users">select * from users where userid = #{suibian}</select><!--添加用户--><insert id="insertUsers" >insert into users value(default ,#{username},#{usersex})</insert><!--预更新用户的查询--><select id="selectUserById2" resultType="com.bjsxt.pojo.Users">select * from users where userid = ${userid}</select>
</mapper>
5.6.3.2 修改 UsersDao 接口
package com.bjsxt.dao;import com.bjsxt.pojo.Users;import java.io.IOException;
import java.util.List;
import java.util.Map;public interface UsersDao {List<Users> selectUsersAll()throws IOException;Users selectUsersById(int userid)throws IOException;void insertUsers(Users users);Users selectUsersById2(int userid);void updateUsersById(Users users);
}
5.6.3.3 修改 UsersDao 接口实现类
package com.bjsxt.dao.impl;import com.bjsxt.dao.UsersDao;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.cursor.Cursor;
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 java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;public class UsersDaoImpl implements UsersDao {/*** 查询所有用户* @return*/@Overridepublic List<Users> selectUsersAll()throws IOException {//创建SqlSessionFactory对象InputStream inputStream = Resources.getResourceAsStream("mybatis-cfg.xml");SqlSessionFactory sqlSessionFacotry = new SqlSessionFactoryBuilder().build(inputStream);//获取SqlSession对象SqlSession sqlSession = sqlSessionFacotry.openSession();//通过SqlSession对象下的API完成对数据库的操作List<Users> list = sqlSession.selectList("com.bjsxt.mapper.UserMapper.selectUsersAll");//关闭SqlSession对象sqlSession.close();return list;}/***  根据用户ID查询用户* @param userid* @return* @throws IOException*/@Overridepublic Users selectUsersById(int userid) throws IOException {//创建SqlSessionFactory对象InputStream inputStream = Resources.getResourceAsStream("mybatis-cfg.xml");SqlSessionFactory sqlSessionFacotry = new SqlSessionFactoryBuilder().build(inputStream);//获取SqlSession对象SqlSession sqlSession = sqlSessionFacotry.openSession();Users users = sqlSession.selectOne("com.bjsxt.mapper.UserMapper.selectUsersById",userid);sqlSession.close();return users;}@Overridepublic void insertUsers(Users users) {SqlSession sqlSession = MybatisUtils.getSqlSession();sqlSession.insert("com.bjsxt.mapper.UserMapper.insertUsers", users);}//预更新用户的查询@Overridepublic Users selectUsersById2(int userid) {SqlSession sqlSession = MybatisUtils.getSqlSession();Users users =  sqlSession.selectOne("com.bjsxt.mapper.UserMapper.selectUserById2",userid);return users;}//更新用户@Overridepublic void updateUsersById(Users users) {SqlSession sqlSession = MybatisUtils.getSqlSession();sqlSession.update("com.bjsxt.mapper.UserMapper.updateUsersById", users);}
}
5.6.3.4 修改 UsersService 接口
package com.bjsxt.service;import com.bjsxt.pojo.Users;import java.util.Map;public interface UsersService {void addUsers(Users users);Users preUpdateUsers(int userid);void modifyUsers(Users users);
}
5.6.3.5 修改 UsersService 接口实现类
package com.bjsxt.service.impl;import com.bjsxt.dao.UsersDao;
import com.bjsxt.dao.impl.UsersDaoImpl;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;import java.util.Map;public class UsersServiceImpl implements UsersService {/*** 添加用户* @param users*/@Overridepublic void addUsers(Users users) {SqlSession sqlSession = MybatisUtils.getSqlSession();try{UsersDao usersDao =  new UsersDaoImpl();usersDao.insertUsers(users);sqlSession.commit();}catch (Exception e){e.printStackTrace();sqlSession.rollback();}finally{MybatisUtils.closeSqlSession();}}@Overridepublic Users preUpdateUsers(int userid) {Users users = null;try{SqlSession sqlSession = MybatisUtils.getSqlSession();UsersDao usersDao = new UsersDaoImpl();users = usersDao.selectUsersById2(userid);}catch(Exception e){e.printStackTrace();}finally {MybatisUtils.closeSqlSession();}return users;}@Overridepublic void modifyUsers(Users users) {SqlSession sqlSession = MybatisUtils.getSqlSession();try{UsersDao usersDao = new UsersDaoImpl();usersDao.updateUsersById(users);sqlSession.commit();}catch(Exception e){e.printStackTrace();sqlSession.rollback();}finally {MybatisUtils.closeSqlSession();}}
}
5.6.3.6 创建测试类
package com.bjsxt.test;import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import com.bjsxt.service.impl.UsersServiceImpl;public class UpdateUsersTest {public static void main(String[] args) {UsersService usersService = new UsersServiceImpl();Users users = usersService.preUpdateUsers(2);System.out.println(users.getUserid());users.setUsername("OLDLU");users.setUsersex("MALE");usersService.modifyUsers(users);}
}

5.6.4 删除用户操作

5.6.4.1 修改映射配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UserMapper"><!--查询所有用户--><select id="selectUsersAll" resultType="com.bjsxt.pojo.Users">select * from users</select><!--根据用户ID查询用户--><select id="selectUsersById" parameterType="int" resultType="users">select * from users where userid = #{suibian}</select><!--添加用户--><insert id="insertUsers" >insert into users value(default ,#{username},#{usersex})</insert><!--预更新用户的查询--><select id="selectUserById2" resultType="com.bjsxt.pojo.Users">select * from users where userid = ${userid}</select><!--更新用户操作--><update id="updateUsersById" >update users set username = #{username},usersex=#{usersex} where userid = #{userid}</update><!-- 根据用户ID删除用户--><delete id="deleteUsersById">delete from users where userid = #{userid}</delete>
</mapper>
5.6.4.2 修改 UsersDao 接口
package com.bjsxt.dao;import com.bjsxt.pojo.Users;import java.io.IOException;
import java.util.List;
import java.util.Map;public interface UsersDao {List<Users> selectUsersAll()throws IOException;Users selectUsersById(int userid)throws IOException;void insertUsers(Users users);Users selectUsersById2(int userid);void updateUsersById(Users users);void deleteUsersById(int userid);
}
5.6.4.3 修改 UsersDao 接口实现类
package com.bjsxt.dao.impl;import com.bjsxt.dao.UsersDao;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.cursor.Cursor;
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 java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;public class UsersDaoImpl implements UsersDao {/*** 查询所有用户* @return*/@Overridepublic List<Users> selectUsersAll()throws IOException {//创建SqlSessionFactory对象InputStream inputStream = Resources.getResourceAsStream("mybatis-cfg.xml");SqlSessionFactory sqlSessionFacotry = new SqlSessionFactoryBuilder().build(inputStream);//获取SqlSession对象SqlSession sqlSession = sqlSessionFacotry.openSession();//通过SqlSession对象下的API完成对数据库的操作List<Users> list = sqlSession.selectList("com.bjsxt.mapper.UserMapper.selectUsersAll");//关闭SqlSession对象sqlSession.close();return list;}/***  根据用户ID查询用户* @param userid* @return* @throws IOException*/@Overridepublic Users selectUsersById(int userid) throws IOException {//创建SqlSessionFactory对象InputStream inputStream = Resources.getResourceAsStream("mybatis-cfg.xml");SqlSessionFactory sqlSessionFacotry = new SqlSessionFactoryBuilder().build(inputStream);//获取SqlSession对象SqlSession sqlSession = sqlSessionFacotry.openSession();Users users = sqlSession.selectOne("com.bjsxt.mapper.UserMapper.selectUsersById",userid);sqlSession.close();return users;}@Overridepublic void insertUsers(Users users) {SqlSession sqlSession = MybatisUtils.getSqlSession();sqlSession.insert("com.bjsxt.mapper.UserMapper.insertUsers", users);}//预更新用户的查询@Overridepublic Users selectUsersById2(int userid) {SqlSession sqlSession = MybatisUtils.getSqlSession();Users users =  sqlSession.selectOne("com.bjsxt.mapper.UserMapper.selectUserById2",userid);return users;}//更新用户@Overridepublic void updateUsersById(Users users) {SqlSession sqlSession = MybatisUtils.getSqlSession();sqlSession.update("com.bjsxt.mapper.UserMapper.updateUsersById", users);}@Overridepublic void deleteUsersById(int userid) {SqlSession sqlSession = MybatisUtils.getSqlSession();final int delete = sqlSession.delete("com.bjsxt.mapper.UserMapper.deleteUsersById", userid);}}
5.6.4.4 修改 UsersService 接口
package com.bjsxt.service;import com.bjsxt.pojo.Users;import java.util.Map;public interface UsersService {void addUsers(Users users);Users preUpdateUsers(int userid);void modifyUsers(Users users);void dropUsersById(int userid);
}
5.6.4.5 修改 UsersService 接口实现类
package com.bjsxt.service.impl;import com.bjsxt.dao.UsersDao;
import com.bjsxt.dao.impl.UsersDaoImpl;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;import java.util.Map;public class UsersServiceImpl implements UsersService {/*** 添加用户* @param users*/@Overridepublic void addUsers(Users users) {SqlSession sqlSession = MybatisUtils.getSqlSession();try{UsersDao usersDao =  new UsersDaoImpl();usersDao.insertUsers(users);sqlSession.commit();}catch (Exception e){e.printStackTrace();sqlSession.rollback();}finally{MybatisUtils.closeSqlSession();}}@Overridepublic Users preUpdateUsers(int userid) {Users users = null;try{SqlSession sqlSession = MybatisUtils.getSqlSession();UsersDao usersDao = new UsersDaoImpl();users = usersDao.selectUsersById2(userid);}catch(Exception e){e.printStackTrace();}finally {MybatisUtils.closeSqlSession();}return users;}@Overridepublic void modifyUsers(Users users) {SqlSession sqlSession = MybatisUtils.getSqlSession();try{UsersDao usersDao = new UsersDaoImpl();usersDao.updateUsersById(users);sqlSession.commit();}catch(Exception e){e.printStackTrace();sqlSession.rollback();}finally {MybatisUtils.closeSqlSession();}}@Overridepublic void dropUsersById(int userid) {SqlSession sqlSession = MybatisUtils.getSqlSession();try{UsersDao usersDao = new UsersDaoImpl();usersDao.deleteUsersById(userid);sqlSession.commit();}catch(Exception e){e.printStackTrace();sqlSession.rollback();}finally{MybatisUtils.closeSqlSession();}}
}
5.6.4.6 创建测试类
package com.bjsxt.test;import com.bjsxt.service.UsersService;
import com.bjsxt.service.impl.UsersServiceImpl;public class DeleteUsersTest {public static void main(String[] args) {UsersService usersService = new UsersServiceImpl();usersService.dropUsersById(3);}
}

三、 Java 日志处理框架

1 常用的日志处理框架

对于一个应用程序来说日志记录是必不可少的一部分。线上问题追踪,错误排查,基于 日志的业务逻辑统计分析等都离不日志。
日志的管理是系统很重要的一部分,千万不可忽略其重要性。完整的日志将会在系统维 护中起着异常重要的作用。
在 Java 领域存在多种日志框架,目前常用的日志框架包括 Log4j,Log4j2,Commons Logging,Slf4j,Logback,Jul。

2 Log4j 简介

Log4j: Log For Java(Java 的日志) 是 Apache 提供的一个开源的 Java 主流的日志框架。

3 Log4j 的日志级别

Log4j 定义了 8 个日志级别(除去 OFF 和 ALL,可以说分为 6 个级别),优先级从高到 低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。

在 Log4j 中建议只使用 DEBUG、INFO、WARN、ERROR 四个日志级别。

  • ALL 最低等级的,用于打开所有日志记录。
  • TRACE designates finer-grained informational events than the DEBUG.Since:1.2.12,很低的 日志级别,一般不会使用。
  • DEBUG 指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打 印一些运行信息。
  • INFO 消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重 要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避 免打印过多的日志。
  • WARN 表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一 些提示。
  • ERROR 指出虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息, 如果不想输出太多的日志,可以使用这个级别。
  • FATAL 指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错 误,这种级别你可以直接停止程序了。
  • OFF 最高等级的,用于关闭所有日志记录。

4 Log4j 的使用

4.1Log4j 配置文件详解

4.1.1 Log4j 配置文件名

log4j 配置文件名:log4j.properties
Log4j 配值文件存放位置:项目的 src 的根目录中

4.1.2 配置根 Logger

log4j.rootLogger = [level],appenderName,appenderName2,...

level 是日志记录的优先级,优先级从低到高分别是 DEBUG,INFO,WARN,ERROR。通过在 这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关,比如在这里定义了 INFO 级别,则应用程序中所有 DEBUG 级别的日志信息将不被打印出来

appenderName 就是指定日志信息输出到哪个地方。可同时指定多个输出目的地。

4.1.3 Log4j 中的 appender

  • org.apache.log4j.ConsoleAppender(输出到控制台)
  • org.apache.log4j.FileAppender(输出到文件)
  • org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
  • org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
  • org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
  • org.apache.log4j.jdbc.JDBCAppender(将日志信息添加数据库中)

4.1.4 向控制台输出的 appender

### appender.console 输出到控制台 ###
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=<%d> %5p (%F:%L) [%t] (%c) - %m%n
log4j.appender.console.Target=System.out

4.1.5 向文件输出的 appender

### appender.logfile 输出到日志文件 ###
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=SysLog.log
log4j.appender.logfile.MaxFileSize=500KB
log4j.appender.logfile.MaxBackupIndex=7
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=<%d> %p (%F:%L) [%t] %c - %m%n

4.1.6 向数据库输出的 appender

log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver=com.mysql.jdbc.Driver
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/bjsxt
log4j.appender.logDB.User=root
log4j.appender.logDB.Password=root
log4j.appender.logDB.Sql=INSERT INTO
logs(project_name,create_date,level,category,file_name,thread_name,line,all_ category,message)values('logDemo','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','% t','%L','%l','%m')

4.1.7 通过包名控制日志输出级别

log4j.logger.org.apache=FATAL
log4j.logger.org.apache.commons=ERROR
log4j.logger.org.springframework=ERROR
log4j.logger.com.bjsxt=ERROR

4.1.8 Log4j 的输出格式

Log4J 采用类似 C 语言中的 printf 函数的打印格式格式化日志信息,打印参数如下:

  • %m 输出代码中指定的消息
  • %p 输出优先级,即 DEBUG,INFO,WARN,ERROR,FATAL
  • %r 输出自应用启动到输出该 log 信息耗费的毫秒数
  • %c 输出所属的类目,通常就是所在类的全名
  • %t 输出产生该日志事件的线程名
  • %n 输出一个回车换行符,Windows 平台为“\r\n”,Unix 平台为“\n”
  • %d 输出日志时间点的日期或时间,默认格式为 ISO8601,也可以在其后指定格式
    如:%d{yyyy 年 MM 月 dd 日 HH:mm:ss,SSS},输出类似:2020 年 05 月 01 日 22:10:28,921
  • %l输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数 如: Testlog.main(TestLog.java:10)
  • %F 输出日志消息产生时所在的文件名称
  • %L 输出代码中的行号
  • %x 输出和当前线程相关联的 NDC(嵌套诊断环境),像 java servlets 多客户多线程的应用 中
  • %%输出一个"%"字符

可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。 如:

  • %5c: 输出 category 名称,最小宽度是 5,category<5,默认的情况下右对齐
  • %-5c:输出 category 名称,最小宽度是 5,category<5,"-"号指定左对齐,会有空格
  • %.5c:输出 category 名称,最大宽度是 5,category>5,就会将左边多出的字符截掉,<5 不会有空格
  • %20.30c:category 名称<20 补空格,并且右对齐,>30 字符,就从左边交远销出的字符截掉

4.2Log4j 的使用方式

4.2.1 Log4j.jar

添加 jar 包:log4j.jar
添加配置文件:log4j.properties
通过 Log4j 的 API 完成日志

import org.apache.log4j.Logger;
public class LogDemo { private final static Logger logger = Logger.getLogger(LogDemo.class);
}

4.2.2 commons-logging + log4j

commons-logging 是 Apache 的 Commons 项目中提供的一个高层的日志框架,是门面模 式的典型应用。commons-logging 本身没有实现真正的日志能力。它可以挂接不同的日志系 统,默认情况下,Commons Loggin 自动搜索并使用 Log4j,如果没有找到 Log4j,再使用JDK。

Logging。
log4j.jar
commons-loggin.jar
项目中常用使用

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class LogDemo { private static final Log logger = LogFactory.getLog(LogDemo.class); }

4.2.3 slf4j-api + slf4j-log4j + log4j

SLF4J 的全称是 Simple Logging Facade for Java,即简单日志门面应用。SLF4J 并不是具体 的日志框架,而是作为一个简单门面服务于各类日志框架,如 java.util.logging, logback 和 log4j。
slf4j-api.jar
slf4j-log4j.jar
log4j.jar

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogDemo { private static final Logger logger = LoggerFactory.getLogger(LogDemo.class);
}

四、 Mybatis 配置的完善

1 Mybatis 的日志管理

Mybatis 的内置日志工厂(LogFactory)提供日志处理功能,内置日志工厂将日志交 给以下其中一种工具作代理:

  • SLF4J
  • Apache Commons Logging
  • Log4j2
  • Log4j
  • JDK logging
  • NO_LOGGING
    MyBatis 内置日志工厂基于运行时自省机制选择合适的日志工具。它会使用第一个查 找得到的工具(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。也就是说在项目中把日志工具环境配置出来后,不用再 MyBatis 进行配置就可以让日志生效。

指定 SLF4J 作为日志处理器

<settings> <setting name="logImpl" value="SLF4J"/>
</settings>

2 使用别名 alias

Mybatis 中的别名配置它只和 XML 配置有关, 只用来减少类完全限定名的多余部分。注 意:别名都是大小写不敏感的。
在配置文件中为类的完整路径定义别名,可以采用两种方式

2.1方式一

使用 typeAlias 指定单个类的别名,在全局配置文件中配置,【注意】:直接放到setting标签之后

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--引入properties文件--><properties resource="db.properties"/><!-- 开启SLF4J做日志处理--><settings><setting name="logImpl" value="SLF4J"/></settings><!--配置别名--><typeAliases><typeAlias type="com.bjsxt.pojo.Users" alias="u"/><package name="com.bjsxt.pojo"/></typeAliases><!--环境的配置--><environments default="development"><environment id="development"><!-- 配置事务--><transactionManager type="JDBC"></transactionManager><!-- 配置数据源--><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="com/bjsxt/mapper/UsersMapper.xml"/></mappers></configuration>
<select id="selectUsersById" parameterType="int" resultType="u"> select * from users where userid = #{suibian}
</select>

2.2方式二

使用 package 指定某个包下所有类的默认别名

<typeAliases> <package name="com.bjsxt.pojo"/>
</typeAliases>

引入别名后的映射文件 <select id="selectU

<select id="selectUsersById" parameterType="int" resultType="users"> select * from users where userid = #{suibian}
</select>

五、 SqlSession 对象下的常用 API

SqlSession 对象下的方法是用来执行定义在映射配置文件中的 SELECT,INSERT,UPDATE 和 DELETE 语句。通过 SqlSession 对象下的 API 向 SQL 语句传递参数时,参数可以是基本数 据类型、包装类类型 ,POJO 或 Map。但是参数的数量只能是一个。

SqlSession 对象下的方法需要 namespace + id 来定位需要执行的 SQL 语句。

namespace + id 的作用: 即:namespace 定位到唯一的 mapper 映射文件,id 定位到这 个 mapper 映射文件的指定的 sql 语句。

1 查询操作

1.1selectOne 方法

<T> T selectOne(String namespace + id , Object parameter)

1.2selectList 方法

<E> List<E> selectList(String namespace + id , Object parameter)

1.3selectMap 方法

<K,V> Map<K,V> selectMap(String namespace + id , Object parameter, String mapKey)

<!--根据用户名与用户性别查询用户--><select id="selectUsersByNameAndSex" resultType="u">select * from users where username =#{name} and usersex = #{sex}</select>

UserDao

 Map<Integer,Users> selectUserByNameAndSex(String  username,String usersex);

UsersDaoImpl

@Overridepublic Map<Integer, Users> selectUserByNameAndSex(String username, String usersex) {SqlSession sqlSession = MybatisUtils.getSqlSession();Map<String,String> param = new HashMap<>();param.put("name",username);param.put("sex",usersex);Map<Integer, Users> users = sqlSession.selectMap("com.bjsxt.mapper.UserMapper.selectUsersByNameAndSex", param, "userid");return users;}

UsersService

 Map<Integer,Users> findUsersByNameAndSex(String username,String usersex);

UsersSecviceImpl

 @Overridepublic Map<Integer, Users> findUsersByNameAndSex(String username, String usersex) {Map<Integer, Users> map = null;try{UsersDao usersDao = new UsersDaoImpl();map = usersDao.selectUserByNameAndSex(username,usersex);}catch(Exception e){e.printStackTrace();}finally{MybatisUtils.closeSqlSession();}return map;}

Test

package com.bjsxt.test;import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import com.bjsxt.service.impl.UsersServiceImpl;import java.util.Map;
import java.util.Set;public class SelectMapTest {public static void main(String[] args) {UsersService usersService = new UsersServiceImpl();Map<Integer, Users> map = usersService.findUsersByNameAndSex("bjsxt", "male");Set<Integer> integers = map.keySet();for(Integer key:integers){Users users = map.get(key);System.out.println(key);System.out.println(users.getUserid()+"\t"+users.getUsername()+"\t"+users.getUsersex());}}
}

2 DML 操作

2.1 insert 方法

int insert(String statement, Object parameter)

2.2 update 方法

int update(String statement, Object parameter)

2.3 delete 方法

int delete(String statement, Object parameter)

六、 Mapper 动态代理

1 基于 Mybatis 的 Dao 层设计

在 Mybatis 中对于 Dao 层的设计提供了两种方式:

1.1 Dao 层不使用 Mapper 动态代理

所谓不使用动态代理是指在 Dao 层需要我们自己来创建 Dao 层的接口与接口实现类。 在接口实现类的方法中我们自己通过调用 SqlSession 对象的方法完成数据库的操作。
目前我们的入门案例就是通过这种方式完成了对 users 表的 CRUD 操作。

Dao 层不使用 Mapper 动态代理缺点:

1.在 SqlSession 对象的常用方法中只能向 SQL 语句中传递一个参数。如果要多个参数, 需要封装到 POJO 或者 Map 中。

2.调用 SqlSession 对象的方法时会有硬编码现象 namespace + id。

1.2 Dao 层使用 Mapper 动态代理

在 MyBatis 中提供了另外一种 Dao 层的实现方式,既:Mapper 动态代理(或称为接口 绑定)的操作方式。这种方式下程序员只需要写 Dao 接口,不需要创建 Dao 的接口实现类, Mybatis 会自动生成接口实现类的代理对象。在 Dao 层我们只要创建接口与映射配置文件即 可。这种方式可以大大简化 Dao 层的代码结构,是在开发中最常见的使用方式。

2 Mapper 动态代理规范

  • 接口名称需要与映射配置文件名称相同
  • 映射配置文件中 namespace 必须是接口的全名。
  • 接口中的方法名和映射配置文件中的标签的 id 一致。
  • 接口中的返回值类型和映射配置文件中的 resultType 的指定的类型一致。

3 Mapper 动态代理的使用

3.1搭建环境

3.1.1 创建项目

3.1.2 添加 jar 包

3.1.3 创建实体

package com.bjsxt.pojo;import java.io.Serializable;public class Users implements Serializable{private int userid;private String username;private String usersex;public int getUserid() {return userid;}public void setUserid(int userid) {this.userid = userid;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getUsersex() {return usersex;}public void setUsersex(String usersex) {this.usersex = usersex;}@Overridepublic String toString() {return "Users{" +"userid=" + userid +", username='" + username + '\'' +", usersex='" + usersex + '\'' +'}';}
}

3.1.4 创建 Mybatis 工具类

package com.bjsxt.utils;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 java.io.IOException;
import java.io.InputStream;public class MybatisUtils {private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();private static SqlSessionFactory sqlSessionFactory = null;static{//创建SqlSessionFactoryInputStream is = null;try{is = Resources.getResourceAsStream("mybatis-cfg.xml");}catch (IOException e){e.printStackTrace();}sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);}//获取SqlSessionpublic static SqlSession getSqlSession(){SqlSession sqlSession = threadLocal.get();if(sqlSession == null){sqlSession = sqlSessionFactory.openSession();threadLocal.set(sqlSession);}return sqlSession;}//关闭SqlSessionpublic static void closeSqlSession(){SqlSession sqlSession = threadLocal.get();if(sqlSession != null){sqlSession.close();threadLocal.set(null);}}
}

3.2配置 Mybatis 框架

3.2.1 添加 db.properteis 文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/bjsxt
jdbc.username=root
jdbc.password=root

3.2.2 添加 log4j.properties 文件

log4j.rootLogger=debug,console,logfile### appender.console输出到控制台 ###
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=<%d> %5p (%F:%L) [%t] (%c) - %m%n
log4j.appender.console.Target=System.out### appender.logfile输出到日志文件 ###
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=SysLog.log
log4j.appender.logfile.MaxFileSize=500KB
log4j.appender.logfile.MaxBackupIndex=7
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=<%d> %p (%F:%L) [%t] %c - %m%n

3.2.3 添加全局配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--引入db.properties属性文件--><properties resource="db.properties"/><settings><setting name="useGeneratedKeys" value="true"/></settings><!--配置别名--><typeAliases><package name="com.bjsxt.pojo"/></typeAliases><!--配置环境--><environments default="development"><environment id="development"><transactionManager type="JDBC"></transactionManager><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><!--引入Mapper映射配置文件--><mappers><package name="com.bjsxt.mapper"/></mappers>
</configuration>

3.2.4 添加 UsersMapper 接口

package com.bjsxt.mapper;import com.bjsxt.pojo.Users;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.RowBounds;import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;public interface UsersMapper {}

3.2.5 添加 UsersMapper 映射配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UsersMapper"></mapper>

3.3实现查询所有用户

3.3.1 修改映射配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UsersMapper"><cache/><!--查询所有用户--><select id="selectUsersAll" resultType="users">select * from users</select></mapper>

3.3.2 修改 UsersMapper 接口添加抽象方法

package com.bjsxt.mapper;import com.bjsxt.pojo.Users;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.RowBounds;import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;public interface UsersMapper {List<Users> selectUsersAll();
}

3.3.3 创建业务层接口

package com.bjsxt.serivce;import com.bjsxt.pojo.Users;import java.util.List;public interface UsersService {List<Users> findUsersAll();}

3.3.4 创建业务层接口实现类

package com.bjsxt.serivce.impl;import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.serivce.UsersService;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;import java.util.List;public class UsersServiceImpl implements UsersService {@Overridepublic List<Users> findUsersAll() {List<Users> list = null;try{SqlSession sqlSession = MybatisUtils.getSqlSession();//getMapper方法的作用是根据给定的接口的Class对象,生成该接口的接口实现类的代理对象UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);list = mapper.selectUsersAll();}catch(Exception e){e.printStackTrace();}finally{MybatisUtils.closeSqlSession();}return list;}
}

3.3.5 创建测试类

package com.bjsxt.test;import com.bjsxt.pojo.Users;
import com.bjsxt.serivce.UsersService;
import com.bjsxt.serivce.impl.UsersServiceImpl;import java.util.List;public class SelectUsersAllTest {public static void main(String[] args) {UsersService usersService = new UsersServiceImpl();List<Users> list = usersService.findUsersAll();//list.forEach(e -> System.out.println(e));list.forEach(System.out::println);//以上两种写法等同于/*for (Users e:list){System.out.println(e);}*/}
}

3.4实现根据用户 ID 查询用户

3.4.1 修改映射配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UsersMapper"><cache/><!--查询所有用户--><select id="selectUsersAll" resultType="users">select * from users</select><!--根据用户ID查询用户--><select id="selectUsersById" resultType="users">select * from users where userid = #{userid}</select>
</mapper>

3.4.2 修改 UsersMapper 接口添加抽象方法

package com.bjsxt.mapper;import com.bjsxt.pojo.Users;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.RowBounds;import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;public interface UsersMapper {List<Users> selectUsersAll();Users selectUsersById(int userid);
}

3.4.3 修改业务层接口

package com.bjsxt.serivce;import com.bjsxt.pojo.Users;import java.util.List;public interface UsersService {List<Users> findUsersAll();Users findUsersById(int userid);
}

3.4.4 修改业务层接口实现类

package com.bjsxt.serivce.impl;import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.serivce.UsersService;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;import java.util.List;public class UsersServiceImpl implements UsersService {@Overridepublic List<Users> findUsersAll() {List<Users> list = null;try{SqlSession sqlSession = MybatisUtils.getSqlSession();//getMapper方法的作用是根据给定的接口的Class对象,生成该接口的接口实现类的代理对象UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);list = mapper.selectUsersAll();}catch(Exception e){e.printStackTrace();}finally{MybatisUtils.closeSqlSession();}return list;}@Overridepublic Users findUsersById(int userid) {Users users = null;try{SqlSession sqlSession = MybatisUtils.getSqlSession();UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);users = mapper.selectUsersById(userid);}catch(Exception e){e.printStackTrace();}finally{MybatisUtils.closeSqlSession();}return users;}
}

3.4.5 创建测试类

package com.bjsxt.test;import com.bjsxt.pojo.Users;
import com.bjsxt.serivce.UsersService;
import com.bjsxt.serivce.impl.UsersServiceImpl;public class SelectUsersByIdTest {public static void main(String[] args) {UsersService usersService = new UsersServiceImpl();Users users = usersService.findUsersById(1);System.out.println(users);}
}

4 Mapper 动态代理模式下的多参数处理

4.1顺序传参法

在映射文件中,SQL 语句中的参数需要使用 arg0,arg1…或者 param1,param2…表示参 数的顺序。此方法可读性低,且要求参数的顺序不能出错,在开发中不建议使用。

修改UserMapper接口

package com.bjsxt.mapper;import com.bjsxt.pojo.Users;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.RowBounds;import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;public interface UsersMapper {List<Users> selectUsersAll();Users selectUsersById(int userid);List<Users> selectUsersOrderParam(String username,String usersex);}

修改Mapper配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UsersMapper"><cache/><!--查询所有用户--><select id="selectUsersAll" resultType="users">select * from users</select><!--根据用户ID查询用户--><select id="selectUsersById" resultType="users">select * from users where userid = #{userid}</select><!--根据用户姓名与性别查询用户,使用顺序传参法--><select id="selectUsersOrderParam" resultType="users">select * from users where username = #{arg0} and usersex= #{arg1}</select>   <!--根据用户姓名与性别查询用户,使用顺序传参法--><select id="selectUsersOrderParam" resultType="users">select * from users where username = #{param1} and usersex= #{param2}</select>
</mapper>

添加测试类

package com.bjsxt.test;import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;import java.util.List;public class SelectUsersOrderParamTest {public static void main(String[] args) {SqlSession sqlSession = MybatisUtils.getSqlSession();UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);List<Users> list = usersMapper.selectUsersOrderParam("bjsxt","male");list.forEach(System.out::println);MybatisUtils.closeSqlSession();}
}

4.2@Param 注解传参法

在接口方法的参数列表中通过@Param 注解来定义参数名称,在 SQL 语句中通过注解中 所定义的参数名称完成参数位置的指定。
此方式在参数不多的情况还是比较直观的,推荐使用。

修改Mapper配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UsersMapper"><cache/><!--查询所有用户--><select id="selectUsersAll" resultType="users">select * from users</select><!--根据用户ID查询用户--><select id="selectUsersById" resultType="users">select * from users where userid = #{userid}</select><!--根据用户姓名与性别查询用户,使用顺序传参法--><select id="selectUsersOrderParam" resultType="users">select * from users where username = #{param1} and usersex= #{param2}</select><!--根据用户姓名与性别查询用户,使用@Param注解传参法--><select id="selectUsersAnnParam" resultType="users">select * from users where username = #{name} and usersex= #{sex}</select></mapper>

修改UsersMapper接口

package com.bjsxt.mapper;import com.bjsxt.pojo.Users;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.RowBounds;import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;public interface UsersMapper {List<Users> selectUsersAll();Users selectUsersById(int userid);List<Users> selectUsersOrderParam(String username,String usersex);List<Users> selectUsersAnnParam(@Param("name") String username,@Param("sex") String usersex);
}

添加测试类

package com.bjsxt.test;import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;import java.util.List;public class SelectUsersAnnParamTest {public static void main(String[] args) {SqlSession sqlSession = MybatisUtils.getSqlSession();UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);List<Users> list = usersMapper.selectUsersAnnParam("bjsxt","male");list.forEach(System.out::println);MybatisUtils.closeSqlSession();}
}

4.3POJO 传参法

在 Mapper 动态代理中也可以使用 POJO 作为传递参数的载体,在 SQL 语句中绑定参数 时使用 POJO 的属性名作为参数名即可。此方式推荐使用。

修改Mapper配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UsersMapper"><cache/><!--查询所有用户--><select id="selectUsersAll" resultType="users">select * from users</select><!--根据用户ID查询用户--><select id="selectUsersById" resultType="users">select * from users where userid = #{userid}</select><!--根据用户姓名与性别查询用户,使用顺序传参法--><select id="selectUsersOrderParam" resultType="users">select * from users where username = #{param1} and usersex= #{param2}</select><!--根据用户姓名与性别查询用户,使用@Param注解传参法--><select id="selectUsersAnnParam" resultType="users">select * from users where username = #{name} and usersex= #{sex}</select><!--根据用户姓名与性别查询用户,使用POJO传参法--><select id="selectUsersPOJOParam" resultType="users">select * from users where username = #{username} and usersex= #{usersex}</select>
</mapper>

修改UsersMapper接口

package com.bjsxt.mapper;import com.bjsxt.pojo.Users;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.RowBounds;import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;public interface UsersMapper {List<Users> selectUsersAll();Users selectUsersById(int userid);List<Users> selectUsersOrderParam(String username,String usersex);List<Users> selectUsersAnnParam(@Param("name") String username,@Param("sex") String usersex);List<Users> selectUsersPOJOParam(Users users);}

添加测试类

package com.bjsxt.test;import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;import java.util.List;public class SelectUsersPOJOParamTest {public static void main(String[] args) {SqlSession sqlSession = MybatisUtils.getSqlSession();UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);Users users = new Users();users.setUsername("bjsxt");users.setUsersex("male");List<Users> list = usersMapper.selectUsersPOJOParam(users);list.forEach(System.out::println);MybatisUtils.closeSqlSession();}
}

4.4 Map 传参法

在 Mapper 动态代理中也可以使用 Map 作为传递参数的载体,在 SQL 语句中绑定参数 时使用 Map 的 Key 作为参数名即可。此方法适合在传递多参数时,如果没有 POJO 能与参数 匹配,可以使用该方式传递参数。推荐使用。

MyBatis 传递 map 参数时,如果传递参数中没有对应的 key 值,在执行 sql 语句时默认 取的是 null。

修改Mapper配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UsersMapper"><cache/><!--查询所有用户--><select id="selectUsersAll" resultType="users">select * from users</select><!--根据用户ID查询用户--><select id="selectUsersById" resultType="users">select * from users where userid = #{userid}</select><!--根据用户姓名与性别查询用户,使用顺序传参法--><select id="selectUsersOrderParam" resultType="users">select * from users where username = #{param1} and usersex= #{param2}</select><!--根据用户姓名与性别查询用户,使用@Param注解传参法--><select id="selectUsersAnnParam" resultType="users">select * from users where username = #{name} and usersex= #{sex}</select><!--根据用户姓名与性别查询用户,使用POJO传参法--><select id="selectUsersPOJOParam" resultType="users">select * from users where username = #{username} and usersex= #{usersex}</select><!--根据用户姓名与性别查询用户,使用Map传参法--><select id="selectUsersMapParam" resultType="users">select * from users where username = #{keyname} and usersex= #{keysex}</select>
</mapper>

修改UsersMapper接口

package com.bjsxt.mapper;import com.bjsxt.pojo.Users;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.RowBounds;import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;public interface UsersMapper {List<Users> selectUsersAll();Users selectUsersById(int userid);List<Users> selectUsersOrderParam(String username,String usersex);List<Users> selectUsersAnnParam(@Param("name") String username,@Param("sex") String usersex);List<Users> selectUsersPOJOParam(Users users);List<Users> selectUsersMapParam(Map<String,String> map);}

添加测试类

package com.bjsxt.test;import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;import java.util.HashMap;
import java.util.List;
import java.util.Map;public class SelectUsersMapParamTest {public static void main(String[] args) {SqlSession sqlSession = MybatisUtils.getSqlSession();UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);Map<String,String> map = new HashMap<>();map.put("keyname","bjsxt");map.put("keysex","male");List<Users> list = usersMapper.selectUsersMapParam(map);list.forEach(System.out::println);MybatisUtils.closeSqlSession();}
}

5 映射配置文件中的特殊字符处理

在 Mybatis 的映射配置文件中不可以使用一些特殊字符,如:<,>

5.1使用符号实体

我们可以使用符号的实体来表示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.UsersMapper"><cache/><!--查询所有用户--><select id="selectUsersAll" resultType="users">select * from users</select><!--根据用户ID查询用户--><select id="selectUsersById" resultType="users">select * from users where userid = #{userid}</select><!--根据用户姓名与性别查询用户,使用顺序传参法--><select id="selectUsersOrderParam" resultType="users">select * from users where username = #{param1} and usersex= #{param2}</select><!--根据用户姓名与性别查询用户,使用@Param注解传参法--><select id="selectUsersAnnParam" resultType="users">select * from users where username = #{name} and usersex= #{sex}</select><!--根据用户姓名与性别查询用户,使用POJO传参法--><select id="selectUsersPOJOParam" resultType="users">select * from users where username = #{username} and usersex= #{usersex}</select><!--根据用户姓名与性别查询用户,使用Map传参法--><select id="selectUsersMapParam" resultType="users">select * from users where username = #{keyname} and usersex= #{keysex}</select><!--查询用户ID大于1的用户--><select id="selectUsers" resultType="users">select * from users where userid &ge; #{userid}</select></mapper>

修改UsersMapper接口

package com.bjsxt.mapper;import com.bjsxt.pojo.Users;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.RowBounds;import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;public interface UsersMapper {List<Users> selectUsersAll();Users selectUsersById(int userid);List<Users> selectUsersOrderParam(String username,String usersex);List<Users> selectUsersAnnParam(@Param("name") String username,@Param("sex") String usersex);List<Users> selectUsersPOJOParam(Users users);List<Users> selectUsersMapParam(Map<String,String> map);List<Users> selectUsers(int userid);}

添加测试类

package com.bjsxt.test;import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;import java.util.List;public class SelectUsersTest {public static void main(String[] args) {SqlSession sqlSession = MybatisUtils.getSqlSession();UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);List<Users> list = usersMapper.selectUsers(1);list.forEach(System.out::println);MybatisUtils.closeSqlSession();}
}

5.2使用 CDATA 区

CDATA:全称为 Character Data,以"<![CDATA[ "内容" ]]>",CDATA 中的内容不会被解析程 序解析。

 <!--查询用户ID大于1的用户--><select id="selectUsers" resultType="users">select * from users where userid <![CDATA[ > ]]> #{userid}</select>

6 Mybatis 的分页查询

6.1使用 RowBounds

RowBounds 是 Mybatis 提供的一个专门处理分页的对象。在 RowBounds 对象中有两个 成员变量:
offset:偏移量,从 0 开始计数
limit:限制条数
使用 RowBounds 进行分页,非常方便,不需要在 SQL 语句中写 limit,即可完成分页 功能。但是由于它是在 SQL 查询出所有结果的基础上截取数据的,所以在数据量大的 SQL 中并不适用,它更适合在返回数据结果较少的查询中使用。

6.1.1 修改映射配置文件

 <!--查询所有数据使用RowBounds实现分页处理--><select id="selectUsersRowBounds" resultType="users">select * from users</select>

6.1.2 修改 UsersMapper 接口

List<Users> selectUsersRowBounds(RowBounds rowBounds);

6.1.3 创建测试类

package com.bjsxt.test;import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;import java.util.List;public class SelectUsersRowBoundsTest {public static void main(String[] args) {SqlSession sqlSession = MybatisUtils.getSqlSession();UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);RowBounds rowBounds = new RowBounds(1,1);List<Users> list = usersMapper.selectUsersRowBounds(rowBounds);list.forEach(System.out::println);MybatisUtils.closeSqlSession();}
}

6.2使用 SQL 语句分页

在分页查询时,如果返回的结果较多,那么需要使用特定的 SQL 语句来实现分页处理。 在 MySQL 数据库中我们可以使用 limit 实现分页。

6.2.1 修改映射配置文件

<!--查询所有数据使用limit实现分页处理--><select id="selectUsersLimit" resultType="users">select * from users limit #{offset},#{limit}</select>

6.2.2 修改 UsersMapper

List<Users> selectUsersLimit(@Param("offset") int offset,@Param("limit") int limit);

6.2.3 创建测试类

package com.bjsxt.test;import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;import java.util.List;public class SelectUsersLimitTest {public static void main(String[] args) {SqlSession sqlSession = MybatisUtils.getSqlSession();UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);List<Users> list = usersMapper.selectUsersLimit(0,1);list.forEach(System.out::println);MybatisUtils.closeSqlSession();}
}

7 Mapper 动态代理模式下的 DML 操作

7.1实现添加用户业务

7.1.1 修改映射配置文件

  <!--添加用户--><insert id="insertUsers" >insert into users values(default ,#{username},#{usersex})</insert>

7.1.2 修改 UsersMapper 接口

 int insertUsers(Users users);

7.1.3 创建测试类

package com.bjsxt.test;import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;public class InsertUsersTest {public static void main(String[] args) {SqlSession sqlSession = MybatisUtils.getSqlSession();UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);Users users = new Users();users.setUsername("itbz");users.setUsersex("male");int flag = usersMapper.insertUsers(users);sqlSession.commit();System.out.println(flag);MybatisUtils.closeSqlSession();}
}

8 主键值回填

在数据库中插入数据时,有时我们是需要获取新数据的主键值。在 Mybatis 中支持主键 值回填,可以让我们更够更方便的获取新添加数据的主键值。

Mybatis 中支持两种方法获取主键:
获取自增主键的值。如:MySQL、SqlServer
获取非自增主键的值。如 Oracle

8.1获取自增主键值

8.1.1 开启自动获取自增主键值

8.1.1.1 局部配置 映射配置文件
 <!--添加用户获取主键值[自增]--><insert id="insertUsersGetKey" useGeneratedKeys="true" keyProperty="userid">insert into users values(default ,#{username},#{usersex})</insert>

修改UsersMapper接口

void insertUsersGetKey(Users users);

添加测试类

package com.bjsxt.test;import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;public class InsertUsersGetKeyTest {public static void main(String[] args) {SqlSession sqlSession = MybatisUtils.getSqlSession();UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);Users users = new Users();users.setUsername("itbz-sxt3");users.setUsersex("male");usersMapper.insertUsersGetKey(users);sqlSession.commit();System.out.println(users.getUserid());MybatisUtils.closeSqlSession();}
}

以上方式是只在局部配置有效,想要全局都可以使用需要配置全局配置文件

8.1.1.2 全局配置

全局配置文件

<settings><setting name="useGeneratedKeys" value="true"/></settings>

映射配置文件

 <!--添加用户获取主键值[自增]--><insert id="insertUsersGetKey" keyProperty="userid">insert into users values(default ,#{username},#{usersex})</insert>

8.1.2 修改 UsersMapper 接口

void insertUsersGetKey(Users users);

8.1.3 创建测试类

package com.bjsxt.test;import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;public class InsertUsersGetKeyTest {public static void main(String[] args) {SqlSession sqlSession = MybatisUtils.getSqlSession();UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);Users users = new Users();users.setUsername("itbz-sxt3");users.setUsersex("male");usersMapper.insertUsersGetKey(users);sqlSession.commit();System.out.println(users.getUserid());MybatisUtils.closeSqlSession();}
}

8.2获取非自增主键值

8.2.1 修改映射配置文件

  <!--添加用户获取主键值[非自增] oracle[order:Before] mysql[order:after]--><insert id="insertUsersGetKey2"><selectKey order="AFTER" keyProperty="userid" resultType="int"><!--在MySQL数据库中获取插入数据的主键的值: SELECT LAST_INSERT_ID(),SELECT @@Identity 是SELECT LAST_INSERT_ID()的同义词。作用是相同的。 -->SELECT LAST_INSERT_ID()</selectKey>insert into users values(default ,#{username},#{usersex})</insert>

8.2.2 修改 UsersMapper 接口

void insertUsersGetKey2(Users users);

8.2.3 创建测试类

package com.bjsxt.test;import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;public class InsertUserGetKey2Test {public static void main(String[] args) {SqlSession sqlSession = MybatisUtils.getSqlSession();UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);Users users = new Users();users.setUsername("itbz-sxt4");users.setUsersex("male");usersMapper.insertUsersGetKey2(users);sqlSession.commit();System.out.println(users.getUserid());MybatisUtils.closeSqlSession();}
}

9 Mapper 动态代理原理

9.1获取代理对象 MapperProxy

@Override
public <T> T getMapper(Class<T> type) { return configuration.getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to theMapperRegistry.");}try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); }
}
public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy);
}

9.2通过 MapperMethod 对象执行对应的操作

public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable { return mapperMethod.execute(sqlSession, args);
}
public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break; }case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); break; }case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param));break; }case SELECT: if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { result = executeForCursor(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + command.getName()); }if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); }return result;
}

MyBatis实战【下】

MyBatis实战【上】相关推荐

  1. mybatis实战教程(mybatis in action),mybatis入门到精通

     目录(?) [-] mybatis实战教程mybatis in action之一开发环境搭建 mybatis实战教程mybatis in action之二以接口的方式编程 mybatis实战教程 ...

  2. 【实战】Spring+Spring MVC+Mybatis实战项目之云笔记项目

    [实战]Spring+Spring MVC+Mybatis实战项目之云笔记项目 一.项目简介           1.项目概述 云笔记,是tmocc上的一个子项目,用于客户进行在线学习记录,分享,收藏 ...

  3. MyBatis实战——前身iBatis、基础环境搭建和如何钓鱼

    (一)前身iBatis 网上发现MyBatis书很少,因为旧项目用到的是ibatis,所以先大致过了iBatis,因为太老,所以基本上讲解的东西好少,但是还有<iBatis实战>这本书,再 ...

  4. mybatis源码阅读(二):mybatis初始化上

    转载自  mybatis源码阅读(二):mybatis初始化上 1.初始化入口 //Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与 ...

  5. 首发 阿里P8整理万字MyBatis实战笔记,精简易懂,开源免费下载

    MyBatis作为国内经常使用的持久层框架,其内部代码的设计非常优秀.比如在开发过程中,有能力对框架进行深度的定制化开发,解决BUG也更加得心应手!另外学习开发者是如何设计高扩展性.低耦合性的代码,便 ...

  6. 快速学习MyBatis|实战项目详解

    作者主页:橙子! 主页 系列专栏:JavaWeb基础教程系列 精彩回顾:HTTP协议详解 文章目的:快速学习MyBatis及实战项目详解 文章目录 1.什么是MyBatis? 2. JDBC存在的缺点 ...

  7. mybatis实战教程(mybatis in action)之三:实现数据的增删改查

    前面已经讲到用接口的方式编程.这种方式,要注意的一个地方就是.在User.xml  的配置文件中,mapper namespace="com.yihaomen.mybatis.inter.I ...

  8. mybatis实战总结

    项目实战使用的技术是spring boot ,mybatis,druid mybatis采用注解的方式,没有采用xml的方式,省去配置的内容. 项目使用maven管理依赖包,数据库使用mysql5.7 ...

  9. Mybatis(史上最详+扩展插件工具使用)

    Mybatis-9.28 环境: JDK1.8 Mysql 5.7 maven 3.6.1 IDEA 回顾: JDBC Mysql Java基础 Maven Junit SSM框架:配置文件的. 最好 ...

最新文章

  1. C#开发Android应用的必备——Mono for Android V1.0 发布
  2. python自动化接口测试中的cookies怎么实现_python接口自动化测试--requests cookies处理...
  3. HDU 5025Saving Tang Monk BFS + 二进制枚举状态
  4. python opencv轮廓检测_OpenCV 轮廓检测的实现方法
  5. 实现开发板与ubuntu的共享--根文件系统NFS--Samba共享【sky原创】
  6. yum 安装rabbitMQ
  7. Linux下汇编语言学习笔记23 ---
  8. 小D课堂-nexus
  9. 讲述:一个月薪 12000 的北京程序员的真实生活
  10. 15000cd是多少流明_光通量(lm)发光强度(cd)照度单位(lux)之间的关系
  11. python 音频文件采样率转换
  12. VC++6.0 Win32应用程序 如何添加窗体 ------阿冬专栏
  13. 西南大学2019春计算机作业答案,2019年西南大学作业答案[1175]《仪器分析》
  14. Windows配置环境变量
  15. 不知哪位仁兄有该软件,csdn好像不提供下载了哦
  16. 快递物流管理系统(SSM,JQUERY-EASYUI,MYSQL)
  17. 硬盘保护技术的原理和实践初探
  18. 移植TFT-LCD到HAL库
  19. 51单片机 c语言小数计算,51单片机怎么实现浮点运算
  20. 若要运行此应用程序,您必须首先安装NET Framework 解决办法

热门文章

  1. BTC不同格式私钥的相互转换
  2. 2019java8u201环境变量_CentOS7.4安装jdk1.8.0_201、Tomcat-8.5.38环境
  3. 基于IDL的高分二号影像批量预处理程序
  4. 谷歌多账户登陆_如何一次登录多个Google帐户
  5. 摩拜创始人套现15亿:你的同龄人,正在抛弃你+韩寒回应
  6. 什么是 Benchmarks?
  7. ubuntu软件开发必备东西
  8. 利用Wifidog实现微信wifi连接以及自写认证服务器
  9. 计算机网络——基本认识
  10. 汽车电子行业 常见英文缩写