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 {
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");
String sql
= "select * from user where username = ?";
preparedStatement
= connection
.prepareStatement(sql
);
preparedStatement
.setString(1, "王五");
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
) {
e
.printStackTrace();
}
}
if (preparedStatement
!= null
) {
try {
preparedStatement
.close();
} catch (SQLException e
) {
e
.printStackTrace();
}
}
if (connection
!= null
) {
try {
connection
.close();
} catch (SQLException e
) {
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();
}
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 ssfb
= new SqlSessionFactoryBuilder();
InputStream inputStream
= Resources
.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory
= ssfb
.build(inputStream
);
SqlSession sqlSession
= sqlSessionFactory
.openSession();
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
;
public class SqlSessionFactoryUtils {
private static SqlSessionFactory sqlSessionFactory
;
static {
try {
SqlSessionFactoryBuilder ssfb
= new SqlSessionFactoryBuilder();
InputStream inputStream
= Resources
.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory
= ssfb
.build(inputStream
);
} catch (IOException e
) {
e
.printStackTrace();
}
}
public static SqlSessionFactory
getSqlSessionFactory() {
return sqlSessionFactory
;
}
}
4.4 编写模糊查询的代码
@Test
public void testGetUserByUserName() {
SqlSessionFactory sqlSessionFactory
= SqlSessionFactoryUtils
.getSqlSessionFactory();
SqlSession sqlSession
= sqlSessionFactory
.openSession();
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
= SqlSessionFactoryUtils
.getSqlSessionFactory();
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
.insert("user.insertUser",user
);
System
.out
.println(user
);
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
= SqlSessionFactoryUtils
.getSqlSessionFactory();
SqlSession sqlSession
= sqlSessionFactory
.openSession();
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
);
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
= SqlSessionFactoryUtils
.getSqlSessionFactory();
SqlSession sqlSession
= sqlSessionFactory
.openSession();
User user
=new User();
user
.setId(56);
user
.setUsername("小东2222");
sqlSession
.update("user.updateUser",user
);
System
.out
.println(user
);
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
= SqlSessionFactoryUtils
.getSqlSessionFactory();
SqlSession sqlSession
= sqlSessionFactory
.openSession();
sqlSession
.delete("user.deleteUser",54);
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
;
public interface UserDao {
User
getUserById(Integer id
);
List
<User> getUserByUserName(String userName
);
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
.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
>