1、为什么学习Mybatis
Mybatis就是类似于hibernate的orm持久层框架。
为什么学Mybatis?
- 目前最主流的持久层框架为hibernate与mybatis,而且国内目前情况使用Mybatis的公司比hibernate要多。
- Hibernate学习门槛不低,要精通门槛更高。门槛高在怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate缓存与数据加载策略方面需要你的经验和能力都很强才行。国内目前前的情况精通hibernate技术大牛非常少。
- sql优化方面,Hibernate的查询会将表中的所有字段查询出来,这一点会有性能消耗。当然了,Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。说得更深入一些,如果有个查询要关联多张表,比如5张表,10张表时,而且,我们要取的字段只是其中几张表的部分字段。这时用hibernate时就会显得非常力不从心。就算用hibernate的sqlquery,后续的维护工作也会让人发狂。
2、JDBC编程回顾与存在的问题分析
2.1 开发步骤
- 导入数据脚本,在课前资料中有
- 创建工程,导入mysql jar包
- 编码
2.2 Jdbc访问数据库的过程
- 加载数据库驱动
- 创建数据库连接
- 创建statement
- 设置sql语句
- 设置查询参数
- 执行查询,得到ResultSet
- 解析结果集ResultSet
- 释放资源
package mybatis.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JdbcTest {
// 加载驱动
// 创建连接
// 设置sql语句
// 创建Statement
// 设置参数
// 执行查询,得到ResultSet
// 遍历ResultSet,输出结果
// 释放资源
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8",
"root", "123456");
// 定义sql语句 ?表示占位符
String sql = "select * from user where username = ?";
// 获取预处理statement
preparedStatement = connection.prepareStatement(sql);
// 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
preparedStatement.setString(1, "王五");
// 向数据库发出sql执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
// 遍历查询结果集
while (resultSet.next()) {
System.out.println(resultSet.getString("id") + " " + resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放资源
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
2.3 Jdbc存在的问题
- 频繁创建和打开、关闭数据连接,太消耗资源
- Sql语句存在硬编码,不利于维护
- Sql参数设置硬编码,不利于维护
- 结果集获取与遍历复杂,存在硬编码,不利于维护,期望能够查询后返回一个java对象
3、Mybatis介绍
- MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
- Mybatis是面向sql的持久层框架,他封装了jdbc访问数据库的过程,我们开发,只需专注于sql语句本身的拼装,其它复杂的过程全部可以交给mybatis去完成。
4、Mybaits入门
4.1 工程搭建
4.1.1 导入依赖的jar包
4.1.2 配置SqlMapConfig.xml
新建一个Source Folder(注意不是Folder)用于存放SqlMapConfig.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">
<!--dtd的模式就是要求标签出现的顺序必须按照其(configuration)顺序来 -->
<configuration>
<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
<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/mybatis?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
</configuration>
4.1.3 配置log4j.properties
在config下创建log4j.properties如下:
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
4.1.4 配置实体类
package mybatis.pojo;
import java.util.Date;
public class User {
private Integer id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(String username, String sex, Date birthday, String address) {
super();
this.username = username;
this.sex = sex;
this.birthday = birthday;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", sex=" + sex + ", birthday="
+ birthday + ", address=" + address + ", uuid2=" + uuid2 + "]";
}
}
4.1.5 配置sql查询的映射文件
粘贴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">
<!-- namespace:命名空间,用于隔离sql语句,后继有重要作用 -->
<!-- #{}:占位符,相当于jdbc的问号? -->
<!-- ${}:字符串拼接指令,如果入参是普通数据类型{ }内部只写value -->
<mapper namespace="user">
<!-- id:sql id,语句的唯一标识 parameterType:入参的数据类型 resultType:返回结果的数据类型 -->
<select id="getUserById" parameterType="int"
resultType="mybatis.pojo.User">
SELECT
`id`,
`username`,
`birthday`,
`sex`,
`address`
FROM `user`
WHERE id=#{id};
</select>
<!-- resultType:如果返回结果为集合时,只需设置为每一个元素的数据类型 -->
<select id="getUserByUserName" parameterType="string"
resultType="mybatis.pojo.User">
SELECT
`id`,
`username`,
`birthday`,
`sex`,
`address`
FROM `user`
<!-- WHERE username LIKE #{name}; -->
WHERE username LIKE '%${value}%';
</select>
</mapper>
4.1.6 加载映射文件
在SqlMapConfig中添加
<!-- 加载映射文件 -->
<mappers>
<!--当前目录下 --!>
<mapper resource="mybatis/user.xml" />
</mappers>
4.2 编写测试代码
@Test
public void testGetUserById() throws Exception {
// 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
// 创建核心配置文件的输入流
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 通过输入流创建SqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory = ssfb.build(inputStream);
// 创建SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行查询,参数一:sql id ;参数二:入参
User user = sqlSession.selectOne("user.getUserById", 1);
// 输出结果
System.out.println(user);
// 释放资源
sqlSession.close();
}
4.3 抽取工具类
package mybatis.utils;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/**
* SqlSessionFactory工具类
* @author Administrator
*
*/
public class SqlSessionFactoryUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
// 创建核心配置文件的输入流
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 通过输入流创建SqlSessionFactory工厂对象
sqlSessionFactory = ssfb.build(inputStream);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 获取SqlSessionFactory
*
* @return
*/
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}
4.4 编写模糊查询的代码
@Test
public void testGetUserByUserName() {
// 获取SqlSessionFactory
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行查询,参数一:sql id ;参数二:入参
//(user.xml配置中的第一种写法)
// List<User> list = sqlSession.selectList("user.getUserByUserName","%张%");
//(user.xml配置中的第二种写法)
List<User> list = sqlSession.selectList("user.getUserByUserName", "张");
// 输出结果
for (User user : list) {
System.out.println(user);
}
// 释放资源
sqlSession.close();
}
4.5 添加用户
4.5.1 编写user.xml配置文件
<!--useGeneratedKeys:使用自增 -->
<!--keyProperty:与之配套使用,这里是user的主键 -->
<!--插入用户 -->
<insert id="insertUser" parameterType="mybatis.pojo.User"
useGeneratedKeys="true" keyProperty="id">
<!-- selectKey:主键返回; -->
<!-- keyProperty:user中的主键属性; -->
<!-- resultType:主键数据类型 ; -->
<!-- order:指定selectKey何时执行;AFTER表示之后 -->
<!-- <selectKey keyProperty="id" resultType="int" order="AFTER"> SELECT
LAST_INSERT_ID(); </selectKey> -->
INSERT INTO `user`
(`username`,
`birthday`,
`sex`,
`address`)
VALUES(
#{username},
#{birthday},
#{sex},
#{address}
) ;
</insert>
注: useGeneratedKeys="true" keyProperty="id"
等价于
<selectKey keyProperty="id" resultType="int" order="AFTER"> SELECT
LAST_INSERT_ID(); </selectKey>
4.5.2 编写测试代码
@Test
public void testInsertUser() throws Exception{
// 获取SqlSessionFactory
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
String str = "2019-03-05";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse(str);
User user=new User("小东2", "1", d, "江苏南京");
/* User user =new User();
user.setUsername("小東");
user.setSex("1");
user.setBirthday(new Date());
user.setAddress("江蘇南京");*/
sqlSession.insert("user.insertUser",user);
System.out.println(user);
// 提交事务,Mybatis默认jdbc事务是不提交的,或者在openSession方法中设置参数为true,表示自动提交
sqlSession.commit();
// 释放资源
sqlSession.close();
}
4.5.3 uuid的使用
假如插入的表没有主键,可以使用uuid给它分配个主键
配置user.xml
注:在使用uuid之前数据库user表要先加上uuid2字段、user的pojo也要加上相应属性
<!--useGeneratedKeys:使用自增 -->
<!--keyProperty:与之配套使用,这里是user的主键 -->
<!--插入用户 -->
<insert id="insertUserUUID" parameterType="mybatis.pojo.User"
useGeneratedKeys="true" keyProperty="id">
<!-- selectKey:主键返回; -->
<!-- keyProperty:user中的主键属性; -->
<!-- resultType:主键数据类型 ; -->
<!-- order:指定selectKey何时执行;AFTER表示之后 -->
<selectKey keyProperty="uuid2" resultType="string" order="BEFORE">
SELECT UUID()
</selectKey>
INSERT INTO `user`
(`username`,
`birthday`,
`sex`,
`address`,
`uuid2`)
VALUES(
#{username},
#{birthday},
#{sex},
#{address},
#{uuid2}) ;
</insert>
注:这里开启了<selectKey keyProperty="uuid2" resultType="string" order="BEFORE">
SELECT UUID()
</selectKey>
useGeneratedKeys="true" keyProperty="id" 就默认不生效了!
编写测试代码
@Test
public void testInsertUserUUID() throws Exception{
// 获取SqlSessionFactory
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
/*String str = "2019-03-05";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse(str);
User user=new User("小东2", "1", d, "江苏南京");*/
User user =new User();
user.setUsername("小222");
user.setSex("1");
user.setBirthday(new Date());
user.setAddress("南京");
sqlSession.insert("user.insertUserUUID",user);
System.out.println(user);
// 提交事务,Mybatis默认jdbc事务是不提交的,或者在openSession方法中设置参数为true,表示自动提交
sqlSession.commit();
// 释放资源
sqlSession.close();
}
4.6 修改用户
4.6.1 编写user.xml文件
<!-- 更新用户 -->
<update id="updateUser" parameterType="mybatis.pojo.User">
UPDATE
`user`
SET
`username` = #{username}
WHERE `id` = #{id};
</update>
4.6.2 编写测试代码
@Test
public void testupdateUser() throws Exception{
// 获取SqlSessionFactory
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
/*String str = "2019-03-05";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse(str);
User user=new User("小东2", "1", d, "江苏南京");*/
User user =new User();
user.setId(56);
user.setUsername("小东2222");
sqlSession.update("user.updateUser",user);
System.out.println(user);
// 提交事务,Mybatis默认jdbc事务是不提交的,或者在openSession方法中设置参数为true,表示自动提交
sqlSession.commit();
// 释放资源
sqlSession.close();
}
4.7 删除用户
4.7.1 编写user.xml文件
<!-- 删除用户 -->
<delete id="deleteUser" parameterType="int">
DELETE
FROM
`user`
WHERE `id` = #{id} ;
</delete>
4.7.2 编写测试代码
@Test
public void testdeleteUser() throws Exception{
// 获取SqlSessionFactory
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
/*String str = "2019-03-05";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse(str);
User user=new User("小东2", "1", d, "江苏南京");*/
sqlSession.delete("user.deleteUser",54);
// 提交事务,Mybatis默认jdbc事务是不提交的,或者在openSession方法中设置参数为true,表示自动提交
sqlSession.commit();
// 释放资源
sqlSession.close();
}
4.8 Mybatis入门小结与Mybatis架构图
5、Mybatis Dao开发方式
5.1 原始Dao开发方法
5.1.1 使用原有的user映射文件,不需修改
5.1.2 新建个UserDao接口
package mybatis.dao;
import java.util.List;
import mybatis.pojo.User;
/**
* 用户信息持久化接口
* @author Administrator
*
*/
public interface UserDao {
/**
* 根据用户ID查询用户信息
* @param id
* @return
*/
User getUserById(Integer id);
/**
* 根据用户名查找用户列表
* @param userName
* @return
*/
List<User> getUserByUserName(String userName);
/**
* 添加用户
* @param user
*/
void insertUser(User user);
}
5.1.3 新建个UserDaoImpl接口实现类
package mybatis.dao.impl;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import mybatis.dao.UserDao;
import mybatis.pojo.User;
import mybatis.utils.SqlSessionFactoryUtils;
public class UserDaoImpl implements UserDao{
@Override
public User getUserById(Integer id) {
SqlSession sqlSession =
SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
User user=sqlSession.selectOne("user.getUserById", id);
sqlSession.close();
return user;
}
@Override
public List<User> getUserByUserName(String userName) {
SqlSession sqlSession =
SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
List<User> list=sqlSession.selectList("user.getUserByUserName", userName);
sqlSession.close();
return list;
}
@Override
public void insertUser(User user) {
SqlSession sqlSession =
SqlSessionFactoryUtils.getSqlSessionFactory().openSession(true);
sqlSession.insert("user.insertUser", user);
//sqlSession.commit();
sqlSession.close();
}
}
5.1.4 使用Dao测试
package mybatis.test;
import java.text.SimpleDateFormat;
import java.util.List;
import org.junit.Test;
import mybatis.dao.UserDao;
import mybatis.dao.impl.UserDaoImpl;
import mybatis.pojo.User;
public class UserDaoImplTest {
@Test
public void testGetUserById() {
UserDao userDao=new UserDaoImpl();
User user=userDao.getUserById(30);
System.out.println(user);
}
@Test
public void testGetUserByUserName() {
UserDao userDao=new UserDaoImpl();
List<User> list=userDao.getUserByUserName("小");
for(User user:list){
System.out.println(user);
}
}
@Test
public void testInsertUser() throws Exception{
UserDao userDao=new UserDaoImpl();
User user=new User("HeadFirst","1",
new SimpleDateFormat("yyyy-MM-dd").parse("2019-3-6"),"上海");
userDao.insertUser(user);
}
}
5.2 官方推荐,接口动态代理
5.2.1 动态代理Dao开发规则
1. namespace必需是接口的全路径名
2. 接口的方法名必需与映射文件的sql id一致
3. 接口的输入参数必需与映射文件的parameterType类型一致
4. 接口的返回类型必须与映射文件的resultType类型一致
5.2.2 动态代理Dao开发步骤
1. 创建UserMapper.xml映射文件(把原来的user.xml复制按开发规则要求修改一下)
2. 创建UserMapper接口(把原来的UserDao.java复制按开发规则要求修改一下)
3. 加载UserMapper.xml
4. 测试动态代理Dao
package mybatis.test;
import java.text.SimpleDateFormat;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import mybatis.mapper.UserMapper;
import mybatis.pojo.User;
import mybatis.utils.SqlSessionFactoryUtils;
public class UserMapperTest {
@Test
public void testGetUserById() {
SqlSession sqlSession =
SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
//获取接口的代理实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(30);
System.out.println(user);
sqlSession.close();
}
@Test
public void testGetUserByUserName() {
SqlSession sqlSession =
SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
//获取接口的代理实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> list=userMapper.getUserByUserName("小");
for(User user:list){
System.out.println(user);
}
sqlSession.close();
}
@Test
public void testInsertUser() throws Exception{
SqlSession sqlSession =
SqlSessionFactoryUtils.getSqlSessionFactory().openSession(true);
//获取接口的代理实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.insertUser(new User("study","2",
new SimpleDateFormat("yyyy-MM-dd").parse("2019-03-07"),"上海虹桥"));
sqlSession.close();
}
}
6、SqlMapConf.xml配置
6.1 properties
6.1.1 属性核心文件配置
<!-- 加载规则,首先加载标签内部属性,再加载外部文件,名称相同时,会替换相同名称的内容
也就是说先加载了root1,加载外部文件时用root替换了 -->
<properties resource="jdbc.properties">
<property name="jdbc.username" value="root1"/>
<property name="jdbc.password" value="123456"/>
</properties>
<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<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>
6.1.2 jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456
6.2 typeAliases
自定义别名:
- 方式一:
<typeAliases>
<!-- 单个别名定义,且别名的使用不区分大小写 -->
<typeAlias type="com.itheima.mybatis.pojo.User" alias="user"/>
</typeAliases>
- 方式二:
<typeAliases>
<!-- 别名包扫描器:别名是类的全称,不区分大小写,适用于多个别名;推荐使用 -->
<package name="com.itheima.mybatis.pojo"/>
</typeAliases>
6.3 mappers
<!-- 加载映射文件 -->
<mappers>
<mapper resource="mybatis/user.xml" />
<!-- <mapper resource="mybatis/UserMapper.xml" /> -->
<!--
映射文件,class扫描器:
1、接口文件必须与映射文件同一目录下
2、接口文件名必须与映射文件的名称一致
(针对单个映射)
-->
<!-- <mapper class="mybatis.mapper.UserMapper"/> -->
<!--
映射文件包扫描:(推荐的方式:针对多个映射)
1、接口文件必须与映射文件同一目录下
2、接口文件名必须与映射文件的名称一致
-->
<package name="mybatis.mapper"/>
</mappers>