1.Mybatis简介
1.1 MyBatis介绍
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上Mybatis对ibatis进行一些改进。 现在托管到gitHub上,下载:https://github.com/mybatis/mybatis-3/releases
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
Mybatis可以将向 preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射)
Mybatis的文档:https://mybatis.org/mybatis-3/zh/getting-started.html
1.2 Mybatis框架执行流程
- 读取MyBatis 配置文件mybatis-config.xml,加载数据源、事务等
- 加载映射文件mapper.xml
- 定义SQL语句,在上一步的文件中加载。
- 创建会话工厂。(SqlSessionFactory)
- 创建会话(SqlSession)
- 通过Executor 操作数据库
- 输入参数和输出结果
2. Mybatis 入门程序
2.1 需求
根据用户id(主键)查询用户信息
根据用户名称模糊查询用户信息
添加用户
删除用户
更新用户
2.2 环境搭建
sql语句:
CREATE TABLE tb_user(
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(20),
password VARCHAR(50),
sex VARCHAR(2),
brithday DATE,
address VARCHAR(200)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
log4j.properties
#在开发阶段使用debug,在生产环境设置为info或者error
log4j.rootLogger=debug,console
#控制台输出
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d %p [%c] - %m%n
添加如下依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.suke</groupId>
<version>1.0-SNAPSHOT</version>
<artifactId>mybatis-demo1</artifactId>
<!--添加mybatis的依赖-->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--mysql的数据库驱动jar-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</project>
2.3 Mybatis的主配置文件 mybatis-config.xml
mybatis-config.xml主要用来配置mybatis的运行环境,数据源、事务等。
在classpath下创建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>
<!-- 和spring整合后 environments配置将废除-->
<environments default="development">
<!-- 可以配置多个environment -->
<environment id="development">
<!-- 使用jdbc事务管理-->
<transactionManager type="JDBC" />
<!-- 数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/shopdb" />
<property name="username" value="root" />
<property name="password" value="123" />
</dataSource>
</environment>
</environments>
</configuration>
注意: 如果使用mysql8de驱动,数据库四大参数:
<environment id="development"> <!-- 使用jdbc事务管理--> <transactionManager type="JDBC" /> <!-- 数据库连接池--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/shopdb?serverTimezone=Asia/Shanghai" /> <property name="username" value="root" /> <property name="password" value="123" /> </dataSource> </environment>
2.4 根据用户id(主键)查询用户信息
编写User实体类
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class User{
//实例类的属性的数据类型写包装类型
private Integer userId;
private String name;
private String password;
private String sex;
//实体类的日期类型一定使用java.util.Date
private Date brithday;
private String address;
}
2.5 sql映射文件
映射文件命名:
User.xml(原始ibatis命名),mapper代理开发映射文件名称叫XXXMapper.xml,比如:UserMapper.xml、ItemsMapper.xml.在映射文件中配置sql语句。
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">
<!-- namespace命名空间,作用就是对sql进行分类管理
注意:使用mapper代理方法开发时,namespace需要特殊设置
-->
<mapper namespace="test">
</mapper>
在UserMapper.xml中添加sql语句
<!-- 根据id获取用户信息 -->
<select id="findUserById" parameterType="int" resultType="org.csmf.mybatis.entity.User">
select * from t_user where id = #{id}
</select>
parameterType:定义输入到sql中的映射类型,#{id}表示使用preparedstatement设置占位符号并将输入变量id传到sql。
resultType:定义结果映射类型。
2.6 将sql映射文件添加到MyBatis主配置文件中
mybatis框架需要加载映射文件,将UserMapper.xml添加到mybatis-config.xml中
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
2.7 测试程序
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.csmf.mybatis.entity.User;
import org.junit.Test;
/**
* 测试使用Mybatis操作数据库
* @author pactera
*
*/
public class UserDaoTest {
@Test
public void testFindUserById(){
InputStream inputStream = null;
try {
//0.加载Mybatis的主配置文件
// Resources.getResourceAsStream()获取classpath下面的资源
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
//1.通过Mybatis的主配置文件得到SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.通过SqlSessionFactory得到SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.通过SqlSession操作数据库
//selectOne():
//第一个参数是Statement的Id也就是sql映射文件中的select,insert,,标签的Id
//第二个参数是输入参数
User user = sqlSession.selectOne("findUserById",2);
//输出
System.out.println(user.getUsername()+"-->"+user.getBrithday());
//4.关闭SqlSession
sqlSession.close();
}
}
2.8 根据用户姓名模糊查询
<!-- 根据姓名模糊查询 -->
<select id="findUserByName" parameterType="string" resultType="org.csmf.mybatis.entity.User">
select * from tb_user where username like #{name}
</select>
2.9 添加用户
<!-- 添加用户 -->
<!--
parameterType: 输入参数为Java对象类型
如果输入参数为java对象类型 #{}中只能写的是Java对象的属性名,不能随意写
注意:如果主键是自增长,不能手动赋值
-->
<insert id="addUser" parameterType="org.csmf.mybatis.entity.User">
insert into tb_user(username,password, sex,birthday, address)
values(#{username},#{password},#{sex},#{brithday},#{address})
</insert>
如果想得到自增列产生的id值,我们可以进行如下配置:
<insert id="insert">
<selectKey resultType="int" order="AFTER" keyProperty="id">
SELECT LAST_INSERT_ID()
</selectKey>
insert into tb_userinfo(name,gender,age,address,email,qq,photo)
values(#{name,jdbcType=VARCHAR},#{gender,jdbcType=VARCHAR},#{age,jdbcType=INTEGER},#{address,jdbcType=VARCHAR}
,#{email,jdbcType=VARCHAR},#{qq,jdbcType=VARCHAR},#{photo,jdbcType=VARCHAR})
</insert>
SELECT LAST_INSERT_ID()
查询生成的id的值,但是需要注意该sql语句必须要与insert的sql语句位于同一次会话中,不然查询不到数据
测试代码
//测试添加用户
@Test
public void testAddUser() throws ParseException{
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setUsername("suke");
user.setAddress("深圳");
user.setPassword("123");
user.setSex("男");
//使用SimpleDateFormat把指定格式的字符串转换为日期
// 1992-10-12
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
user.setBrithday(fmt.parse("1992-10-12"));
sqlSession.insert("test.addUser", user);
//提交事务
sqlSession.commit();
//关闭SqlSession
sqlSession.close();
}
2.10 修改用户
<!-- 修改用户 -->
<update id="updateUser" parameterType="org.csmf.mybatis.entity.User">
update t_user set username=#{username},password=#{password},sex=#{sex},
brithday=#{brithday},address=#{address} where id=#{id}
</update>
测试代码:
// 测试修改用户
@Test
public void testUpdateUser() throws ParseException {
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setId(15);
user.setUsername("王五");
user.setAddress("长沙");
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
user.setBrithday(fmt.parse("1991-11-12"));
user.setPassword("123");
user.setSex("男");
//修改
sqlSession.update("updateUser", user);
// 提交事务
sqlSession.commit();
// 关闭SqlSession
sqlSession.close();
}
3. Mybatis的Dao开发
使用Mybatis开发Dao,通常有两个方法,即原始Dao开发方法和Mapper接口开发方法。
3.1 Mybatis的核心对象
-
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory生产,所以可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是静态代码块中的局部变量
-
SqlSessionFactory
SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。
-
SqlSession
-
SqlSession是一个面向用户的接口, sqlSession中定义了数据库操作,默认使用DefaultSqlSession实现类。
-
SqlSession的实现类即DefaultSqlSession,此对象中对操作数据库实质上用的是Executor
-
每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
-
打开一个 SqlSession;使用完毕就要关闭它。通常把这个关闭操作放到 finally 块中以确保每次都能执行关闭。
-
3.2 传统的DAO开发模式
程序员需要写dao接口和dao实现类。
需要向dao实现类中注入SqlSessionFactory,在方法体内通过SqlSessionFactory创建SqlSession
步骤:
- 编写到接口以及实现类
import java.util.List; public interface IUserDao { /** * 根据ID查询User * @param id 用户Id * @return 用户信息 * @throws Exception */ public User findUserById(int id) throws Exception; /** * 根据用户的名字模糊查询 * @param name 用户的username * @return 一组用户 * @throws Exception */ public List<User> findUserByName(String name) throws Exception; /** * 添加用户 * @param user 用户信息 * @return 包含主键的用户 * @throws Exception */ public User addUser(User user) throws Exception; }
public class UserDaoImpl implements IUserDao { //需要往UserDaoImpl注入SQLSessionFactory private SqlSessionFactory sqlSessionFactory; //通过构造方法注入 public UserDaoImpl(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; } @Override public User findUserById(int id) throws Exception { /** * 1.得到SqlSession * 2.调用SqlSession对应的方法(selectOne)来操作数据库 * 3.关闭SqlSession * 4.返回结果 */ SqlSession sqlSession = sqlSessionFactory.openSession(); User user = sqlSession.selectOne("test.findUserById", id); sqlSession.close(); return user; } @Override public List<User> findUserByName(String name) throws Exception { /** * 1.得到SqlSession * 2.调用SqlSession对应的方法(selectList)来操作数据库 * 3.关闭SqlSession * 4.返回结果 */ SqlSession sqlSession = sqlSessionFactory.openSession(); List<User> users = sqlSession.selectList("test.findUserByName", "%"+name+"%"); sqlSession.close(); return users; } @Override public void addUser(User user) throws Exception { /** * 1.得到SqlSession * 2.调用SqlSession对应的方法(insert)来操作数据库 * 3.提交事务 * 4.关闭SqlSession */ SqlSession sqlSession = sqlSessionFactory.openSession(); sqlSession.insert("test.addUser", user); sqlSession.commit(); sqlSession.close(); } }
编写SQL映射文件
<!-- 根据ID查询 --> <select id="findUserById" parameterType="int" resultType="org.csmf.mybatis.entity.User"> select * from t_user where id = #{id} </select> <!-- 根据姓名模糊查询 --> <select id="findUserByName" parameterType="string" resultType="org.csmf.mybatis.entity.User"> select * from t_user where username like #{name} </select> <!—添加用户--> <insert id="addUser" parameterType="org.csmf.mybatis.entity.User"> <selectKey order="AFTER" resultType="int" keyProperty="id"> select last_insert_id() </selectKey> insert into t_user(username,password,sex,brithday,address) values(#{username},#{password},#{sex},#{brithday},#{address}) </insert>
总结:
原始Dao开发中存在以下问题:
Dao方法体存在重复代码:通过SqlSessionFactory创建SqlSession,调用SqlSession的数据库操作方法
调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不得于开发维护。
3.3 Mapper代理模式
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
- 在mapper.xml中namespace写的是对应Mapper接口的全限定名
Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
实现:
UserMapper.xml:
UserMapper接口:
测试代码:
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.csmf.mybatis.entity.User;
import org.junit.Before;
import org.junit.Test;
/**
* 测试UserMapper的测试类
* @author pactera
*
*/
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp(){
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserById() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建UserMapper的休息,Mybatis可以自动生成UserMapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findUserById(2);
System.out.println(user);
}
}
总结:
- selectOne和selectList
动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。
- namespace
mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用包装类或map对象,保证dao的通用性。