参考:【itheima】
[toc]
mybatis概述
jdbc问题总结
- 没有数据库连接池,浪费资源
- sql硬编码,无法维护
mybatis概述
- 是apache的项目
- 对jdbc进行封装
- xml+各种statement,最终将sql执行结果映射成java对象返回
- 官网中文文档地址:http://www.mybatis.org/mybatis-3/zh/index.html
mybatis架构
![Mybatis01 Mybatis01]()
mybatis配置
- SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
- mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
入门程序
搭建
文件结构
- mybatis-3.2.7.jar—-mybatis的核心包
- lib—-mybatis的依赖包
- mybatis-3.2.7.pdf—-mybatis使用手册
jar包:
- mybatis核心包,依赖包,数据驱动包
log4j.properties
在classpath下创建log4j.properties如下:
Code1
2
3
4
5
6# 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%nmybatis默认使用log4j作为输出日志信息。
SqlMapConfig.xml
xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<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://10.211.55.6:3306/mybatis?characterEncoding=utf-8" />
<property name="username" value="machine" />
<property name="password" value="4869" />
</dataSource>
</environment>
</environments>
</configuration>PO类
Po类作为 mybatis进行sql映射使用,po类通常与数据库表对应
Code1
2
3
4
5
6
7
8
9
10public class User {
private int id;
private String username;
private String sex;
private Date birthday;
private String address;
//set,get...
}
sql映射文件
在classpath下的sqlmap目录下创建sql映射文件Users.xml
Code1
2
3
4
5
6
7
8
9
10
11
12
13
14<?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 namespace="test">
<!--
namespace: 用来隔离sql语句
例如:User user = sqlSession.selectOne("test.findUserById", 10);
-->
</mapper>
```
- 加载映射文件
- 添加在SqlMapConfig.xml下Code1
2
3
4
## 根据id查询用户信息
- user.xml,在mapper下//查询多例 ListCode1
2list = sqlSession.selectList("test.findUserByName", "t");
System.out.println(list);
1 |
|
1 |
sqlSession.selectOne(“test.insertUser”,user);
//提交事务
sqlSession.commit();
//…
1 |
|
1 |
1 |
|
1 |
//若有的字段不想更新,必须先查询先前的值,再注入进去,不然(不写的话),那个字段的值会变空
sqlSession.update(“test.updateUser”,user);
sqlSession.commit();
1 |
|
INSERT INTO User(username,birthday,sex,address)
VALUES(#{username},#{birthday},#{sex},#{address})1 |
user.setUsername(“maxiao”);
user.setBirthday(new Date());
user.setSex(“女”);
user.setAddress(“合肥”);
System.out.println(“=====”+user.getId());
sqlSession.insert(“test.insertUser”,user);
//提交事务
sqlSession.commit();
System.out.println(“=====”+user.getId());
1 |
|
<insert id="insertUser" parameterType="com.machine.po.User" >
<!--before: 将uuid的值注入po,再插入数据库-->
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
select uuid()
</selectKey>
INSERT INTO User(id,username,birthday,sex,address)
VALUES(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
Code1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
## Mybatis解决jdbc编程的问题
- 有数据库链接池
- sql与java代码分离
- 自动将java对象映射至sql语句,parameterType
- 自动将sql执行结果映射至java对象,resultType
## mybatis与hibernate不同
- Mysql需自己编写sql语句,不完全是一个ORM框架
- 原生态sql,灵活度高,适合对关系数据模型要求不高的软件开发(互联网软件、企业运营类软件)
- 无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大
- Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。
- Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
- 所以框架只有适合才是最好
## SqlSession的使用范围
- SqlSessionFactoryBuilder
- SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,所以可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即**方法体内局部变量**。
- SqlSessionFactory
- SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以**单例**模式管理SqlSessionFactory。
- SqlSession
- SqlSession是一个面向用户的接口, sqlSession中定义了数据库操作方法。
- 每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
- 打开一个 SqlSession;使用完毕就要关闭它。通常把这个关闭操作放到 finally 块中以确保每次都能执行关闭。
# DAO开发方法
## 原始Dao开发方法(不推荐)
- 程序员编写Dao接口和Dao实现类
public interface UserDao {
public User getUserById(int id);
public void InsertUser(User user);
}
Code1
public class UserDaoImpl implements UserDao{
//注入sqlSessionFactory
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory){
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User getUserById(int id) {
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = null;
try {
user = sqlSession.selectOne("test.findUserById",id);
}finally {
sqlSession.close();
}
return user;
}
@Override
public void InsertUser(User user) {
SqlSession sqlSession = sqlSessionFactory.openSession();
try{
sqlSession.insert("test.insertUser",user);
sqlSession.commit();
}finally {
sqlSession.close();
}
}
}
Code1
@Test
public void testDao(){
UserDao userDao = new UserDaoImpl(sqlsessionFactory);
User user = userDao.getUserById(1);
System.out.println(user);
User user2 = new User();
user2.setUsername("er");
user2.setSex("女");
user2.setBirthday(new Date());
user2.setAddress("西雅图");
userDao.InsertUser(user2);
}
Code1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 原始dao开发存在以下问题
- 代码重复:通过SqlSessionFactory创建SqlSession,调用SqlSession的数据库操作方法
- 调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不利于开发维护
## Mapper接口开发方法(推荐)
- Mapper动态代理方式
- 开发规范
- 程序员只需编写Mapper接口(相当于Dao接口),由Mybatis框架 根据接口定义 创建接口的**动态代理**对象,代理对象的方法体同上边Dao接口实现类方法。
- Mapper接口开发需要遵循以下规范:
1. Mapper.xml文件中的namespace与mapper接口的类路径相同。
2. Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3. Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
4. Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
- Mapper.xml(映射文件)
- 定义mapper映射文件UserMapper.xml(内容同Users.xml),需要修改namespace的值为 UserMapper接口路径。将UserMapper.xml放在classpath 下mapper目录 下。
<?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接口路径-->
<mapper namespace="com.machine.mapper.UserMapper">
<select id="findUserById" parameterType="int" resultType="com.machine.po.User">
SELECT * FROM User where id = #{id}
</select>
<select id="findUserByName" parameterType="java.lang.String" resultType="com.machine.po.User">
SELECT * FROM User where username LIKE '%${value}%'
</select>
<insert id="insertUser" parameterType="com.machine.po.User" >
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO User(username,birthday,sex,address)
VALUES(#{username},#{birthday},#{sex},#{address})
</insert>
</mapper>
Code1
2
3
- XxxMapper.java(接口文件)
public interface UserMapper {
public User findUserById(int id) throws Exception;
public List<User> findUserByName(String username) throws Exception;
public void insertUser(User user) throws Exception;
}
Code1
2
- 加载UserMapper.xml文件(核心文件中)
<mapper resource="mapper/UserMapper.xml" />
Code1
2
- 测试代码
@Test
public void testMapper1() throws Exception {
SqlSession sqlSession = sqlsessionFactory.openSession();
//获取mapper接口的 代理对象 ,
// sqlSession.getMapper()返回的是对象 ,用接口接受
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
System.out.println("=====按id查找");
User user = userMapper.findUserById(2);
System.out.println(user);
System.out.println("=====按name查找");
List<User> users = userMapper.findUserByName("m");
System.out.println(users);
System.out.println("=====添加");
User user2 = new User();
user2.setUsername("tt");
user2.setAddress("马达加斯加");
user2.setBirthday(new Date());
user2.setSex("男");
userMapper.insertUser(user2);
//提交事务,关闭session
sqlSession.commit();
sqlSession.close();
}
Code1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- mybatis官方推荐使用mapper代理方法开发mapper接口
# SqlMapConfig.xml配置文件
## 配置内容
- 顺序如下:
```t
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
properties(属性)
在classpath下定义db.properties文件
Code1
2
3
4
5
6
7jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://10.211.55.6:3306/mybatis?characterEncoding=utf-8
jdbc.username=machine
jdbc.password=4869
```
- SqlMapConfig.xml引用如下:<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>Code1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- MyBatis 将按照下面的顺序来加载属性:
- 在 properties 元素体内定义的属性首先被读取
- 然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性
## typeAliases(类型别名)
- mybatis支持别名
```t
别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
map Map自定义别名
在SqlMapConfig.xml中配置:
Code1
2
3
4
5
6
7
8
9<typeAliases>
<!--单个定义别名-->
<typeAlias alias="user" type="com.machine.po.User" />
<!--
使用扫描法 批量定义别名
定义后,别名=类名,不区分大小写,单建议按java命名规则(userMapper)
-->
<package name="com.machine.po" />
</typeAliases>
mappers(映射器)
<mapper resource=" " />- 使用相对于类路径的资源,
- 如:
<mapper resource="sqlmap/User.xml" />
- 如:
- 使用相对于类路径的资源,
<mapper class=" " />- 使用mapper接口类路径,
- 如:
<mapper class="com.machine.mapper.UserMapper"/>
- 如:
- 注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中
- 使用mapper接口类路径,
<package name=""/>- 注册指定包下的所有mapper接口,
- 如:
<package name="com.machine.mapper"/>
- 如:
- 注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中
- 注册指定包下的所有mapper接口,





