MyBatis简介
MyBatis 是一款持久层框架,它避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 映射成数据库中的记录【半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射】。
数据持久化
数据持久化是指将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数型据库来完成。一般的框架中,一般在DAO层来完成持久化工作的代码块。
简而言之:Mybatis就是帮助程序猿 将数据存入数据库中 , 和从数据库中取数据。
使用步骤
首先,代码结构是这样子的:
1. 搭建实验数据库
CREATE DATABASE `mybatis`;
USE `mybatis`;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(20) NOT NULL,
`name` varchar(30) DEFAULT NULL,
`pwd` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `user`(`id`,`name`,`pwd`) values (1,'狂神','123456'),(2,'张三','abcdef'),(3,'李四','987654');
2. 创建Maven项目
创建一个Maven项目,删除src目录
注意:最好将编译器使用的maven 修改为自己下载的maven
3. 导入MyBatis相关 jar 包【pom.xml中添加如下代码】
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
4. 创建一个子模块
创建之后,在原来的pom.xml文件中就会自动生成modules标签,表示这些子模块都会共用父亲工程中pom文件的配置
5. 编写MyBatis核心配置文件mybatis-config.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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_test"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="userMapper.xml"/>
</mappers>
</configuration>
6. 编写MyBatis工具类
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 SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession连接
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}
7. 创建实体类
public class User {
private int id; //id
private String name; //姓名
private String pwd; //密码
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String toString(){
StringBuffer sb = new StringBuffer();
sb.append("id");
sb.append(this.id);
sb.append("name");
sb.append(this.name);
sb.append("pwd");
sb.append(this.pwd);
return sb.toString();
}
}
8. 编写Mapper接口类
import java.util.List;
public interface UserMapper {
List<User> selectUser();
}
9. 编写userMapper.xml配置文件
<?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="UserMapper">
<select id="selectUser" resultType="User">
select * from user
</select>
</mapper>
10. 编写测试类【记得先导入Junit哦】
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class MyTest {
@Test
public void selectUser() {
SqlSession session = MybatisUtils.getSession();
//方法一:
//List<User> users = session.selectList("com.kuang.mapper.UserMapper.selectUser");
//方法二:
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for (User user : users) {
System.out.println(user);
}
session.close();
}
}
11. 运行
鼠标点击selectUser方法,然后右键-run as junit,运行,开始运行你的第一个程序吧~
Mybatis执行过程和作用域
作用域:
- SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建SqlSessionFactory 的方法中,而不要让其长期存在。因此
SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域
(也就是局部方法变量)。 - SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为
SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期
。 - 由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说
SqlSessionFactory 的最佳作用域是应用作用域
。 - SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭。所以
SqlSession 的最佳的作用域是请求或方法作用域
。
增删改查操作
查找select
根据id和name查询用户
- 在UserMapper中添加对应方法
public interface UserMapper {
//根据id和name查询用户
User selectUserByIdName(int id, String name);
}
- 在UserMapper.xml中添加Select语句
<select id="selectUserByIdName" resultType="com.kuang.pojo.User">
select * from user where id = #{id} and name = #{name}
</select>
- 测试类中测试
@Test
public void tsetSelectUserById() {
SqlSession session = MybatisUtils.getSession(); //获取SqlSession连接
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserByIdName(1,"sxy");
System.out.println(user);
session.close();
}
如果要向sql语句传递参数,有两种方法:
- 方法1:直接在在接口方法的参数前加 @Param属性
User selectUserByIdName(@Param("id")int id, @Param("name")String name);
这种方式就不用在mapper.xml文件中再设置参数类型
<select id="selectUserByIdName" resultType="com.kuang.pojo.User">
select * from user where id = #{id} and name = #{name}
</select>
- 方法2:使用万能的Map
User selectUserByIdName(Map<String,Object> map);
这种方式需要在mapper.xml文件中设置参数类型为map
<select id="selectUserByIdName" parameterType="map" resultType="com.kuang.pojo.User">
select * from user where id = #{id} and name = #{name}
</select>
使用时,就创建一个Map,存入值,然后调用方法即可。
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", 1);
map.put("name","sxy");
User user = mapper.selectUserByIdName(map);
插入insert
会了select之后,其他三种使用起来都很类似,但是,要注意的是,对于增、删、改操作,最后一定要提交事务!!!
int addUser(User user);
<insert id="addUser" parameterType="com.kuang.pojo.User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(5,"王五","zxcvbn");
int i = mapper.addUser(user);
System.out.println(i);
session.commit(); //提交事务,重点!不写的话不会提交到数据库
session.close();
修改update
类似
<update id="updateUser" parameterType="com.kuang.pojo.User">
update user set name=#{name},pwd=#{pwd} where id = #{id}
</update>
删除delete
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>
模糊查询
方法一:在Java代码中添加sql通配符
string wildcardname = “%smi%”;
mapper.selectlike(wildcardname);
<select id=”selectlike”>
select * from foo where bar like #{value}
</select>
方法二:在sql语句中拼接通配符,会引起sql注入
string wildcardname = “smi”;
list<name> names = mapper.selectlike(wildcardname);
<select id="selectlike">
select * from foo where bar like "%"#{value}"%"
</select>
补充:什么是SQL 注入?
SQL 注入其实就是 恶意用户 通过 在表单
中填写包含 SQL 关键字
的数据来使数据库执行 非常规 代码的过程。
最简单的例子:
假设网页有一个输入框,功能:根据输入框中的名字name,从数据库student中查找数据,即:
name = "sxy"
sql = "select * from student where name = " + name
这个时候,如果有一个恶意用户,输入的名称为sxy;drop table student;
此时,sql语句就成了:sql = "select * from student where name = sxy;drop table student; "
这个时候,你的student表就被删除啦~
分页查询
如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。
-
方法一:使用Limit实现分页
语法:SELECT * FROM table LIMIT stratIndex,pageSize
比如:
SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15 【pageSize为-1表示到最后一行】
SELECT * FROM table LIMIT 5; //检索前 5 个记录行【如果只有一个参数,表示返回最大的记录行数目】
使用:
xml文件中使用select语句:<select>select * from user limit #{startIndex},#{pageSize}</select>
Java使用SQLsession对象执行语句时传入两个参数即可:int currentPage = 1; //第几页 int pageSize = 2; //每页显示几个 Map<String,Integer> map = new HashMap<String,Integer>(); map.put("startIndex",(currentPage-1)*pageSize); map.put("pageSize",pageSize);
-
方法二:使用
RowBounds
在Java代码层面实现分页【不推荐使用】
RowBounds rowBounds = new RowBounds((currentPage-1)*pageSize,pageSize);
List<User> users = session.selectList("getUserByRowBounds", null, rowBounds);
- 方法三:使用第三方插件
比如:PageHelper
配置解析
mybatis-config.xml
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
configuration元素
xml配置文件最外一层就是<configuration></configuration>
environments元素
使用environments可以配置MyBatis的多套运行环境【指定多个environment】,将SQL映射到多个不同的数据库上,必须指定其中一个为默认运行环境(通过default指定)。
使用模板:
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="..." value="..."/>
</dataSource>
</environment>
</environments>
dataSource元素
数据源dataSource用于配置数据库信息,包含在environment元素之下,类型主要包括以下四种形式:
- unpooled:每次被请求时打开和关闭连接
- pooled:利用“池”的概念将 JDBC 连接对象组织起来
- jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用
- 第三方实现:比如dbcp,c3p0,druid
mappers元素
定义 映射SQL语句 的文件。
使用模板:
- 在mybatis-config.xml文件中配置mappers
<mappers>
<mapper resource="com/kuang/dao/userMapper.xml"/>
</mappers>
resource路径引入资源方式有:
- 使用相对于类路径的资源引用:
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
- 使用完全限定资源定位符(URL):
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
- 使用映射器接口实现类的完全限定类名:
<mapper class="org.mybatis.builder.AuthorMapper"/>
- 将包内的映射器接口实现全部注册为映射器,但是需要配置文件名称和接口名称一致,并且位于同一目录下:
<package name="org.mybatis.builder"/>
- 在对应的mapper文件中配置类和sql语句
<mapper namespace="com.kuang.mapper.UserMapper">
<select id="..." parameterType="..." resultType="..."></select>
<insert...></insert>
<update...></update>
<delete...></delete>
</mapper>
注意:namespace的命名必须跟某个接口同名,接口中的方法与映射文件中sql语句id应该一一对应
properties 元素
在 属性文件 中配置 参数,再通过 properties 元素的子元素来传递
- 配置db.properties文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8
username=root
password=123456
- 将文件导入properties 配置文件,在使用参数处,使用
${参数名称}
来获取值【下面的代码中只体现了重点】
<configuration>
<!--导入properties文件-->
<properties resource="db.properties"/>
...
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
...
</configuration>
typeAliases元素
类型别名,为 Java 类型设置一个短的名字,之后再使用的时候,可以直接使用这个别名。
使用模板:
<typeAliases>
<typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
当对包名使用别名时,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
每一个在包 com.kuang.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名
来作为它的别名。
若有注解,则别名为其注解值。比如:
@Alias("user")
public class User {
...
}
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>
参考链接:https://mp.weixin.qq.com/s/vy-TUFa1Rb69ekxiEYGRqw