一、Mybatis 简介
1、什么是MyBatis?
- MyBatis 是一款优秀的持久层框架,
- 它支持定制化 SQL、存储过程以及高级映射。
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息。
Maven仓库获得Mybatis:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
2、数据的持久化
数据的持久化就是把程序中的数据从瞬时状态转化为持久状态的过程。
二、编写第一个MyBatis程序
搭建环境
数据库环境:
CREATE DATABASE `mybatis`;
USE `mybatis`;
CREATE TABLE `user`(
`id` INT(20) NOT NULL PRIMARY KEY,
`name` VARCHAR(30) DEFAULT NULL,
`pwd` VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user`(`id`,`name`,`pwd`) VALUES
(1,'李磊','123456'),
(2,'赵巷','123456'),
(3,'王静怡','11235813')
Maven导入依赖:
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.19</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
编写配置文件: 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?
useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="11235813"/>
</dataSource>
</environment>
</environments>
</configuration>
编写数据库工具类
package com.zyh.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 SqlSessionFactory sqlSessionFactory;
static {
String resource = "mybatis-config.xml";
{
try {
InputStream inputStream = Resources.getResourceAsStream(resource);
//工厂模式
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
}
//获取连接
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}
编写一个实体类:使用lombok注解
package com.zyh.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
编写持久层的操作接口:
package com.zyh.mapping;
import com.zyh.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> getUserList();
}
编写持久层接口的配置文件
<?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">
<!--namespace的作用:和你编写的持久层的接口关联起来-->
<mapper namespace="com.zyh.mapping.UserMapper">
<select id="selectBlog" resultType="Blog">
select * from `user`;
</select>
</mapper>
将你编写的配置文件注册到 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?useUnicode=true&characterEncoding=utf8&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="11235813"/>
</dataSource>
</environment>
</environments>
<!--写好的接口配置文件注册到mybatis-config.xml 配置文件中-->
<mappers>
<mapper resource="com/zyh/mapping/UserMapper.xml"/>
</mappers>
</configuration>
测试:
import com.zyh.mapping.UserMapper;
import com.zyh.pojo.User;
import com.zyh.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class Test {
public static void main(String[] args) {
// 1、获取SqlSession连接,用于执行sql
SqlSession sqlSession = MyBatisUtils.getSession();
//2、通过session.getMapper(class) 获取接口
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}
}
执行结果:
三、生命周期和作用域
图示:
1、SqlSessionFactoryBuilder类
SqlSessionFactory 的创建,需要使用SqlSessionFactoryBuilder对象的build()方法。由于SqlSessionFactoryBuilder 对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。
2、SqlSessionFactory接口
SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的,所以一个应用只需要一个该对象即可。创建SqlSession需要使用SqlSessionFactory接口的的openSession()方法。
- 可以被理解为数据库的连接池。
- SqlSessionFactary 一旦被创建,就应该在应用的运行期间一直存在,不应该丢弃或创建一个新的实例。
3、 SqlSession接口
- SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一次会话以 SqlSession 对象的创建开始,以SqlSession对象的关闭结束。
- SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close() 方法,将其关闭。 再次需要会话,再次创建。而在关闭时会判断当前的SqlSession是否被提交:若没有被提交,则会执行回滚后关闭;若已被提交,则直接将 SqlSession 关闭。所以,SqlSession 无需手工回滚。
四、CRUD操作
1、select 查询
编写接口代码:
public interface UserMapper {
//根据用户的id查询用户
User getUserById(int id);
}
编写配置文件
<mapper namespace="com.zyh.mapping.UserMapper">
<select id="getUserById" parameterType="int" resultType="com.zyh.pojo.User">
select * from mybatis.user where id = #{id};
</select>
</mapper>
测试:
public class Test {
public static void main(String[] args) {
SqlSession session = MyBatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
session.close();
}
执行结果:
2、insert 插入数据
编写接口:
//添加一个用户
int addUser(User user);
编写配置文件:
<insert id="addUser" parameterType="com.zyh.pojo.User">
insert into `user`(`id`,`name`,`pwd`) values (#{id},#{name},#{pwd});
</insert>
测试:
public class Test {
public static void main(String[] args) {
SqlSession sqlSession = MyBatisUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
/* 增删改必须开启事务 */
int i = mapper.addUser(new User(4,"张成西","11235813"));
sqlSession.commit();
if (i > 0) {
System.out.println("添加用户成功");
}
}
}
3、update 修改数据库数据
编写接口:
//修改用户信息
int updateUser(User user);
编写配置文件:
<update id="updateUser" parameterType="com.zyh.pojo.User">
update `user` set `name` = #{name}, pwd = #{pwd} where id = #{id};
</update>
测试:
public class Test {
public static void main(String[] args) {
SqlSession sqlSession = MyBatisUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
/* 增删改必须开启事务 */
int i = mapper.updateUser(new User(4, "haha", "123456"));
sqlSession.commit();
if (i > 0) {
System.out.println("修改成功!");
}
}
}
4、delete 删除数据
编写接口:
//删除用户
int deleteUserById(int id);
编写配置文件:
<delete id="deleteUserById" parameterType="int">
delete from `user` where `id` = #{id};
</delete>
测试:
public class Test {
public static void main(String[] args) {
SqlSession sqlSession = MyBatisUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
/* 增删改必须开启事务 */
int i = mapper.deleteUserById(4);
sqlSession.commit();
if (i > 0) {
System.out.println("删除成功!");
}
}
}
五、配置文件解析
mybatis-config.xml 文件
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)<!--绑定mapper配置文件-->
properties 用来引用本地配置文件
编写一个db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&useSSL=true&characterEnicoding=utf8
username=root
pwd=123456
在配置文件中引用:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${pwd}"/>
</dataSource>
</environment>
</environments>
<!--写好的接口配置文件注册到mybatis-config.xml 配置文件中-->
<mappers>
<package name="com.zyh.mapping"></package>
</mappers>
</configuration>
在mybatis-config.xml中给包配置别名:
<properties resource="db.properties"></properties>
<typeAliases>
<!--这个包下的所有实体类都会被自动扫描到-->
<!--<package name="com.zyh.pojo"></package>-->
<!--给这个包下的User类起一个别名 User,每次只能改一个-->
<typeAlias type="com.zyh.pojo.User" alias="User"></typeAlias>
</typeAliases>
配置 settings,开启日志
<properties resource="db.properties"></properties>
<!--settings大小写不要写错-->
<settings>
<!--配置日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
六、解决数据库字段名和实体类属性名不一致问题(ResultMap)
如果实体类属性和数据库中的字段值一致,Mybatis 就会自动推断并映射,这叫做自动映射。
如果不一致们就需要我们进行手动映射。
数据库中的字段名:
实体类中的字段名:
password 字段属性值和数据库不一致。我们编写好测试代码后查询用户信息发现:
发现 password 为 null。
解决方案:
-
给SQL语句查询时为 password 起一个别名
select id,name,pwd as password from user where id = #{id};
-
结果集映射
编写接口:
package com.zyh.mapping;
import com.zyh.pojo.User;
public interface RsMapper {
User selectUserById(int id);
}
编写接口配置文件:
<?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">
<!--namespace的作用:和你编写的持久层的接口关联起来-->
<mapper namespace="com.zyh.mapping.RsMapper">
<!--结果集映射resultMap -->
<resultMap id="UserMap" type="User">
<!--主键一般使用id标签-->
<id column="id" property="id"/>
<!--column 对应数据库中的字段名 property对应实体类中的属性名-->
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="selectUserById" resultMap="UserMap">
select * from `user` where `id`= #{id};
</select>
</mapper>
测试类:
import com.zyh.mapping.RsMapper;
import com.zyh.pojo.User;
import com.zyh.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
public class Test2 {
public static void main(String[] args) {
SqlSession sqlSession = MyBatisUtils.getSession();
RsMapper mapper = sqlSession.getMapper(RsMapper.class);
User user = mapper.selectUserById(3);
System.out.println(user);
}
}
执行结果:
这次数据库中的pwd
字段的值就被读取出来了。
七、日志和分页
1、日志
日志的作用: 在我们的工作中,如果出现了错误,日志就可以帮助我们排除错误。
MyBatis 的默认日志:
<settings>
<!--配置日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
但目前使用最多的就是 Apache 的 Log4j 日志。
使用Log4j
步骤:
- 导入包依赖:去 Maven 仓库 下载。
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 在MyBatis 核心配置文件中,设置为LOG4J日志
<settings>
<!--配置日志 LOG4J 要求为大写-->
<setting name="logImpl" value="LOG4J"/>
</settings>
- 测试:打印输出的时候就会后日志文件。
使用Log4J输出信息:
import com.zyh.mapping.RsMapper;
import com.zyh.pojo.User;
import com.zyh.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
public class Test2 {
static Logger logger = Logger.getLogger(Test2.class);
public static void main(String[] args) {
SqlSession sqlSession = MyBatisUtils.getSession();
RsMapper mapper = sqlSession.getMapper(RsMapper.class);
User user = mapper.selectUserById(3);
System.out.println(user);
//Log4j 将日志设置为较为详细的输出
logger.error("111111111111"); //输出的错误信息
logger.debug("2222222222222"); //debug 信息
logger.info("333333333333333"); //输出的提示信息
}
}
2、分页
limit 分页:SQL层面实现分页
分页SQL:
select * from `user` limit #{startIndex}, #{pageSize};
接口配置文件:
<?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">
<!--namespace的作用:和你编写的持久层的接口关联起来-->
<mapper namespace="com.zyh.mapping.RsMapper">
<!--结果集映射resultMap -->
<resultMap id="UserMap" type="User">
<!--主键一般使用id标签-->
<id column="id" property="id"/>
<!--column 对应数据库中的字段名 property对应实体类中的属性名-->
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from `user` limit #{startIndex}, #{pageSize};
</select>
</mapper>
测试:
import com.zyh.mapping.RsMapper;
import com.zyh.pojo.User;
import com.zyh.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import java.util.HashMap;
import java.util.List;
public class Test2 {
static Logger logger = Logger.getLogger(Test2.class);
public static void main(String[] args) {
SqlSession sqlSession = MyBatisUtils.getSession();
RsMapper mapper = sqlSession.getMapper(RsMapper.class);
int currentPage = 1; //当前页面
int pageSize = 2; //每页显示几个
HashMap<String,Integer> map = new HashMap<String, Integer>();
map.put("startIndex",(currentPage-1) * pageSize);
map.put("pageSize",pageSize);
List<User> users = mapper.getUserByLimit(map);
mapper.getUserByLimit(map);
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
}
执行结果:
RowBounds 分页 Java 层面实现
接口:
/**
* RowBounds 分页
*/
List<User> getUserByRowBounds();
接口配置文件:
<select id="getUserByRowBounds" parameterType="map" resultMap="UserMap">
select * from `user`;
</select>
测试:
import com.zyh.pojo.User;
import com.zyh.utils.MyBatisUtils;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class Test3 {
public static void main(String[] args) {
SqlSession session = MyBatisUtils.getSession();
//RowBounds分页,从第一个用户开始,每页两个用户数据
RowBounds rowBounds = new RowBounds(0,2);
//Java代码实现分页
List<User> userList = session.selectList("com.zyh.mapping.RsMapper.getUserByRowBounds",null,rowBounds);
for (User user : userList) {
System.out.println(user);
}
session.close();
}
}
执行结果: