文章目录
MyBatis——基础用法
框架一览:
(1)解决技术整合问题的框架:Spring框架是由于软件开发的复杂性而创建的。
(2)解决WEB层问题的MVC框架:SpringMVC。
(3)解决数据的持久化问题的框架:MyBatis是一个基于Java的持久层框架,而Hibernate是一个封装程度更高的持久层框架。
持久化的方式:数据库(JDBC), IO文件(浪费资源)
为什么要进行持久化:因为内存有断电即失的特性,而有些对象我们不希望丢失,这时就需要进行持久化处理。
Mybatis官网文档:https://mybatis.org/mybatis-3/
Mybatis——github源码:https://github.com/mybatis/mybatis-3
Maven依赖:maven仓库
mybatis:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
单元测试junit
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
mysql
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
注:
Mybatis原名iBatis
Mybatis使用步骤;
- (1) 编写实体类
- (2) 编写核心配置类
- (3) 编写接口
- (4) 编写接口对应的Mapper.xml
- (5) 测试
1 mybatis框架(持久层框架)
mybatis是一个基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理 加载驱动、创建连接、创建statement 等繁杂的过程,从而大大简化了JDBC的一系列操作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通 Java 对象)为数据库中的记录。 采用ORM思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了JDBC API底层访问细节,使我们不用与JDBC API打交道,就可以完成对数据库的持久化操作。
ORM(Object Relational Mapping):
对象关系映射,数据表和面向对象语言中的实体类对象互相映射转换。
注:
POJO:简单理解为不包含业务逻辑的单纯用来存储数据的 java类,POJO没有实现任何接口,没有继承任何父类.
优点:
- 简单易学,比较灵活。
- sql和代码分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的orm字段映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql.
MyBatis是对JDBC的进一步封装,JDBC编程的大体流程如下:
1). 创建数据库连接Connection
2). 创建操作命令Statement
3). 使用操作命令来执行SQL
4). 处理结果集ResultSet
5). 释放资源
MyBatis的大体流程:
Mybatis工作流程:
1)读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
2)加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
3)构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
4)创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
5)Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
6)MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
7)输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
8)输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。
2 Mybatis生命周期及作用域:
理解不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder
:
- 一旦创建了 SqlSessionFactory,就不再需要它了
- 局部变量。
SqlSessionFactory
- 相当于是数据库连接池。
- 一旦被创建就应该在应用的运行期间一直存在,不需要创建多个实例
- 最佳作用域是应用作用域。
- 最简单的就是使用 单例模式 或者静态单例模式。
SqlSession
- 连接到连接池的一个请求。
- 用完之后需要关闭close,否则资源被占用
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
3 Mybatis的应用实例一
1)前置准备
创建数据库以及表,创建maven项目,并配置数据库连接。
在pom.xml中导入mybatis、mysql-connector-java、junit(单元测试)依赖包。
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)编写mybatis配置文件mybatis.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?useSSL=true&
useUnicode=true&chracterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="mysql"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/glp/dao/UserMapper.xml"/>
</mappers>
</configuration>
该mybatis配置类用于配置数据库连接,以及注册Mapper.xml(类似接口实现类)。
注意:
resource资源路径为com/glp/dao/UserMapper.xml
而不能写成com.glp.dao.UserMapper.xml
3)编写mybatis工具类utils
package com.glp.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 MyUtils {
private static SqlSessionFactory sqlSessionFactory;
//拿到工厂类
static{
try {
String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//拿到工厂类后,获取SqlSession实例
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
通过SqlSessionFactoryBuilder().build()
拿到一个工厂类SqlSessionFactory
,通过这个工厂类获取SqlSession实例,用来执行后续方法的调用。
4)根据数据库表创建实体类
package com.glp.POJO;
public class User {
private int id;
private String name;
private String pwd;
//省略了 getter,setter
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
5)编写接口类
package com.glp.dao;
import com.glp.POJO.User;
import java.util.List;
public interface UserMapper {
List<User> getUserList();
}
6) 编写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接口-->
<mapper namespace="com.glp.dao.UserMapper" >
<!--id对应接口方法-->
<select id="getUserList" resultType="com.glp.POJO.User">
select * from mybatis.user;
</select>
</mapper>
Dao层查询数据,本应该定义一个实现类来完成,现在我们只需要定义一个接口,在xml中直接写sql语句就可以了,此时这个xml中的Usermapper.xml就相当于是UserMapper接口的实现类。
注:
命名空间namespace中指定的包名,要和接口类一致。
7) 编写junit包测试
package com.glp.dao;
import com.glp.POJO.User;
import com.glp.utils.MyUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserDaoTest {
@Test
public void test(){
//获得SqlSession对象
SqlSession sqlSession = MyUtils.getSqlSession();
try{
//执行SQL
//方式一 推荐
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper .getUserList();
//方式二 了解
// List<User> userList = sqlSession.selectList("com.glp.dao.UserMapper.getUserList");
for (User user : userList) {
System.out.println(user);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭sqlSession
sqlSession.close();
}
}
}
注意:
在build中配置resource,来防止资源导出失败的问题,主要是默认资源目录需要放到resource目录下才可以被正常导出,而如果我们放到其他目录,此时就需要在pom.xml中手动配置资源过滤。
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
4 Mybatis的应用实例二——增删改查CRUD
在实例一的基础上,进行操作。
1)在接口UserMapper中增加方法
package com.glp.dao;
import com.glp.POJO.User;
import java.util.List;
public interface UserMapper {
List<User> getUserList();
//查找
User getUserById(int id);
//增加
int insertUser(User user);
//更新
int updateUser(User user);
//删除
int deleteUser(int id);
}
2)在UserMapper.xml中添加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">
<!--Dao层查询数据,本应该定义一个实现类来完成,现在我们只需要定义一个接口,在xml中直接写sql就可以了-->
<!--此时这个xml中的mapper就相当于是UserDao的实现类-->
<!--绑定一个Mapper接口-->
<mapper namespace="com.glp.dao.UserMapper" >
<!--id对应接口方法-->
<select id="getUserList" resultType="com.glp.POJO.User">
select * from mybatis.user;
</select>
<select id="getUserById" parameterType="int" resultType="com.glp.POJO.User">
select * from mybatis.user where id = #{id};
</select>
<!--对象中的属性可以直接取出来-->
<insert id="insertUser" parameterType="com.glp.POJO.User" >
insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd});
</insert>
<update id="updateUser" parameterType="com.glp.POJO.User">
update mybatis.user set name =#{name},pwd=#{pwd} where id=#{id};
</update>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id = #{id};
</delete>
</mapper>
注:
parameterType
:指定参数类型resultType
:返回类型#{}:
相当于一个占位符- 参数为对象时,可以直接映射为sql中的参数:
<insert id="insertUser" parameterType="com.glp.POJO.User" >
insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd});
</insert>
3)编写测试类
package com.glp.dao;
import com.glp.POJO.User;
import com.glp.utils.MyUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserDaoTest {
@Test
public void test(){
//获得SqlSession对象
SqlSession sqlSession = MyUtils.getSqlSession();
try{
//执行SQL
//方式一 推荐
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper .getUserList();
for (User user : userList) {
System.out.println(user);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭sqlSession
sqlSession.close();
}
}
//查找
@Test
public void getUserById(){
//获得SqlSession对象
SqlSession sqlSession = MyUtils.getSqlSession();
try{
//执行SQL
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper .getUserById(1);
System.out.println(user);
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭sqlSession
sqlSession.close();
}
}
//增删改需要提交事务
@Test
public void insertUser(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int ret = mapper.insertUser(new User(5, "李朗", "131"));
if(ret>0){
System.out.println("插入成功");
}
//增删改必须要提交事务
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateUser(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int ret = mapper.updateUser(new User(2,"关羽","53553"));
if(ret>0){
System.out.println("更新耿工");
}
sqlSession.commit();
sqlSession.close();
}
@Test
public void deleteUser(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int ret = mapper.deleteUser(3);
if(ret>0){
System.out.println("删除成功");
}
sqlSession.commit();
sqlSession.close();
}
}
注意:
- 增删改,都需要用
sqlSession.commit();
提交事务,否则增删改不成功。
5 应用实例三——在接口方法中使用map传递参数
1)定义接口:
Map<String,Object>:
int insertUser2(Map<String,Object> map);
User getUserById2(Map<String,Object> map);
2)编写UserMapper.xml文件
<!--使用map来填充参数-->
<insert id="insertUser2" parameterType="map" >
insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd});
</insert>
<select id="getUserById2" parameterType="map" resultType="com.glp.POJO.User">
select * from mybatis.user where id=#{id};
</select>
此时的参数类型为map.
3)测试:
@Test
public void insertInto2(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String,Object>();
map.put("id",7);
map.put("name","刘备");
map.put("pwd","434");
int ret = mapper.insertUser2(map);
sqlSession.commit();;
sqlSession.close();
}
@Test
public void selectById2(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String,Object>();
map.put("id",1);
User user = mapper.getUserById2(map);
System.out.println(user);
}
将UserMapper.xml中的参数名,当作key,放到map中即可。
注:
如果参数过多,我们可以考虑直接使用Map实现,如果参数比较少,直接传递参数即可
6 模糊查询需要避免sql注入
1)定义接口
List<User> selectLike(String value);
2)UserMapper.xml
<select id="selectLike" resultType="com.glp.POJO.User">
select * from mybatis.user where name like "%"#{str}"%";
</select>
上面这种是不推荐的,因为可能会引起sql注入(存在字符串拼接问题)。
推荐使用下面这种,用concat
进行字符串拼接:
<select id="selectLike" resultType="com.glp.POJO.User">
select * from mybatis.user where name like concat('%',#{str},'%');
</select>
注:
能够解决sql注入又能在配置文件中写%,可以借助mysql中的concat函数。
3)测试
@Test
public void selectLike(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.selectLike("张");
for (User user : list) {
System.out.println(user);
}
}
注意:
-
#{}
能够防止SQL注入 : 在模糊查询中,要这样写like concat(‘%'#{name}'%’)
如果不这样写,仅仅是加上单引号‘%#{name}%’
,那么就当成是一个字符串。 -
#{ }
是预编译处理,MyBatis在处理#{ }
时,它会将sql中的#{ }
替换为?
,然后调用PreparedStatement
的set方法来赋值,传入字符串后,会在值两边加上单引号。 -
预编译的机制: 预编译是提前对SQL编译之前进行预编译,而其后注入的参数将不会再进行编译。 因为SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作。所以预编译机制则可以很好的防止SQL注入。
-
${}
无法防止SQL注入:like ‘%${name}%’
,通过$
的方式拼接的sql语句,不再是以占位符的形式生成sql,而是以拼接字符串的方式生成sql,这样做带来的问题是:会引发sql注入的问题。${}
底层是由Statement实现,只是简单的替换,传递的参数会被当成sql语句中的一部分。 -
$ { }
是字符串替换, MyBatis在处理$ { }
时,它会将sql中的${ }
替换为变量的值,传入的数据不会加两边加上单引号。
7 分页
方式一:推荐
select * from user limit startIndex,pageSie;
select *from user limit 3; #[0,n]
(1) 接口
public interface UserMapper {
//分页
List<User> getUserByLimit(HashMap<String,Object> map);
}
(2)UserMapper.xml
<select id="getUserByLimit" parameterType="map" resultMap="user">
select * from mybatis.user limit #{startIndex},#{pageSize};
</select>
(3) 测试
@Test
public void getUserByLimit(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String,Object> map = new HashMap<String,Object>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> list = mapper.getUserByLimit(map);
for(User user:list){
System.out.println(user);
}
sqlSession.close();
}
方式二:RowBounds分页(了解即可)
(1)mapper接口
//选择全部用户RowBounds实现分页
List<User> getUserByRowBounds();
(2)mapper.xml
<select id="getUserByRowBounds" resultType="user">
select * from user
</select>
(3)测试
@Test
public void testUserByRowBounds() {
SqlSession session = MybatisUtils.getSession();
int currentPage = 2; //第几页
int pageSize = 2; //每页显示几个
RowBounds rowBounds = new RowBounds((currentPage-1)*pageSize,pageSize);
//通过session.**方法进行传递rowBounds,[此种方式现在已经不推荐使用了]
List<User> users = session.selectList("com.kuang.mapper.UserMapper.getUserByRowBounds", null, rowBounds);
for (User user: users){
System.out.println(user);
}
session.close();
}
(3) 方式三:分页插件pagehelper
分页插件pagehelper
8 使用注解
注解开发的本质:反射
底层:动态代理
(1) 在接口上加入注解
//注解
@Select("select * from user")
List<User> getUsers();
(2)需要在核心配置类中绑定接口
<mappers>
<mapper class="com.glp.dao.UserMapper"/>
</mappers>
要想注解和xml配置一起使用要满足下面的条件:
- 接口和它的Mapper配置文件必须同名。
- 接口和它的Mapper配置文件必须在同一个包下。
(3) 测试
@Test
public void getUsers(){
SqlSession sqlSession = MyUtils.getSqlSession();
//执行SQL
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper .getUsers();
for(User user :list){
System.out.println(user);
}
//关闭sqlSession
sqlSession.close();
}
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
(4) 设置自动提交commit
在openSession(true);
中传入true; 注意尽量不要设置自动提交。
public class MyUtils {
private static SqlSessionFactory sqlSessionFactory;
//拿到工厂类
static{
try {
String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//拿到工厂类后,获取SqlSession实例
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
}
(5) 注解实现CURD
1)接口
@Select("select * from user where id = #{id}")
User getUserById2(@Param("id") int id);
@Insert("insert into user values(#{id},#{name},#{password})")
int addUser(User user);
@Update("update user set name=#{name}, pwd=#{password} where id = #{id}")
int updateUser(User user);
@Delete("delete from user where id=#{id}")
int deleteUser(@Param("id") int id);
注:
-
基本类型的数据或者String类型都要加上Param注解
-
方法包含多个参数时,每个参数必须加上
Param
注解, -
引用类型不用写
@Param
-
@Param(“id”)中的属性名id就是我们在SQL中引用的属性名
2 ) 测试
@Test
public void getUsers2(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper .getUserById2(1);
System.out.println(user);
sqlSession.close();
}
@Test
public void insertUser(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(new User(8,"黄忠","444"));
sqlSession.close();
}
@Test
public void updateUser(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.updateUser(new User(3,"吕布","444"));
sqlSession.close();
}
@Test
public void deleteUser(){
SqlSession sqlSession = MyUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser(3);
sqlSession.close();
}