Mybatis基础
typora-copy-images-to: mybatis
typora-root-url: mybatis
Mybatis
一 MyBatis介绍
MyBatis 本是apache**的一个开源项目iBatis**, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
IBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java****的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
mybatis提供一种“**半自动化**”的ORM实现。MyBatis需要手动写SQL,后期可以****!这里的“半自动化”,是相对Hibernate等提供了全面的数据库封装机制的“全自动化”ORM实现而言,“全自动”ORM实现了POJO和数据库表之间的映射,以及 SQL 的自动生成和执行。
MyBatis手动挡汽车
Hibernate自动挡汽车
下载地址: MyBatis下载地址:https://github.com/mybatis/mybatis-3/releases
使用版本:3.4.5
二 MyBatis特点
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql(SQL动态拼接)
性能: JDBC > MyBatis(半自动) > Hibernate(全自动)
三 MyBatis基础应用
3.1 搭建MyBatis环境
3.1.1 环境准备
- Jdk环境:jdk1.8
- Ide环境:eclipse
- 数据库环境:MySQL 5.1
- Mybatis:3.4.5
3.1.2下载MyBatis
<!-- myBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- Junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
3.1.3添加日志配置-log4j.properties
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 [%t] %-5p [%c] - %m%n
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
3.1.4 数据库准备
3.1.5 创建主配置文件: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>
<!-- 引入外部配置文件 -->
<properties resource="jdbc.properties"/>
<!-- 环境 -->
<environments default="development">
<environment id="development">
<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>
<!--映射Mapper文件-->
</configuration>
jdbc.properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatisdb?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123
3.2 实现MyBatis的CRUD
3.2.1 获取SqlSession对象(核心对象)
MyBatis框架中涉及到的几个API
SqlSessionFactoryBuilder:该对象负责根据MyBatis配置文件mybatis-config.xml构建SqlSessionFactory实例 负责生产session
SqlSessionFactory:每一个MyBatis的应用程序都以一个SqlSessionFactory对象为核心。该对象负责创建SqlSession对象实例。
SqlSession:该对象包含了所有执行SQL操作的方法,用于执行已映射的SQL语句
3.2.2 创建实体类
public class User {
private Integer id;
private String username;
private String birthday;
private Integer sex;
private String 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 getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", birthday=" + birthday + ", sex=" + sex + ", address="
+ address + "]";
}
/**
*
*/
public User() {
super();
// TODO Auto-generated constructor stub
}
/**
* @param username
* @param birthday
* @param sex
* @param address
*/
public User(String username, String birthday, Integer sex, String address) {
super();
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
}
3.2.3 创建接口层 UserMapper
/**
* Class Name: UserDao.java
* Description:
* @author dragon DateTime 2018年11月1日 下午4:24:51
* @company bvit
* @email [email protected]
* @version 1.0
*/
public interface UserMapper {
public List<User> getUsers();
public User getUserById(int id);
public int updateAddressById(Map<String,Object> map);
public int deleteUserById(int id);
public int addUser(User user);
}
3.2.4 创建接口UserMapper映射文件
<?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="com.dream.dao.UserMapper">
<select id="getUsers" resultType="com.dream.bean.User">
select * from user
</select>
<select id="getUserById" parameterType="int" resultType="com.dream.bean.User">
select * from user where id=#{id}
</select>
<update id="updateAddressById" parameterType="map">
update user set address=#{address} where id=#{id}
</update>
<delete id="deleteUserById" parameterType="int">
delete from user where id=#{odsad}
</delete>
<insert id="addUser" parameterType="com.dream.bean.User" useGeneratedKeys="true" keyProperty="id">
insert into user values(null,#{username},#{birthday},#{sex},#{address})
</insert>
</mapper>
3.2.5测试
首先在Mybatis主配置文件中配置Mapper映射路径
<mappers>
<!-- 扫描mapper的包 -->
<mapper resource="com/dream/dao/UserMapper.xml"/>
</mappers>
因为sessionFactory和session调用会多次用到,写到程序里会造成代码冗余,所以我们封装一个工具类来创建sqlsessionfactory和获取session
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream;
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSession() {
return sqlSessionFactory.openSession(true); //自动提交事务
}
public static void close(SqlSession sqlSession) {
if (sqlSession != null) {
sqlSession.close();
sqlSession = null;
}
}
}
测试类
test.java
public class Test02 {
@Test
public void test1() {
//1.获取user集合
SqlSession session = MyBatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void test2() {
//1.获取user
SqlSession session = MyBatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User userById = mapper.getUserById(10);
System.out.println(userById);
}
@Test
public void test3() {
//1.更新user
SqlSession session = MyBatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
Map<String, Object> map=new HashMap<String, Object>();
map.put("address", "成都");
map.put("id", 10);
int updateAddressById = mapper.updateAddressById(map);
System.out.println(updateAddressById);
}
@Test
public void test4() {
//1.删除user
SqlSession session = MyBatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int deleteUserById = mapper.deleteUserById(10);
System.out.println(deleteUserById);
}
@Test
public void test5() {
//1.增加user
SqlSession session = MyBatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User("asdsad","2016-5-2", 1, "陈鼓的");
int addUser = mapper.addUser(user);
System.out.println(user);
}
}
3.2.6 使用动态代理总结
使用**mapper接口****不用写接口实现类即可完成数据库操作,使用非常简单,也是官方所推荐的使用方法。
使用mapper接口的必须具备以几个条件:
1) Mapper的namespace必须和mapper接口的全路径一致。
2) Mapper接口的方法名必须和sql定义的id一致。
3) Mapper接口中方法的输入参数类型必须和sql定义的parameterType一致。
4) Mapper接口中方法的输出参数类型必须和sql定义的resultType一致。
四 Mybatis-Config配置
-
properties 属性
-
settings 设置
-
typeAliases 类型别名
-
typeHandlers 类型处理器
-
objectFactory 对象工厂
-
plugins 插件
-
environments 环境
-
environment 环境变量
-
transactionManager 事务管理器
-
dataSource 数据源
-
mappers 映射器
Mybatis的配置文件中配置项是有顺序的,即按照上面的顺序;
4.1 Properties
4.2 typeAliases(别名)
类型别名是为 Java 类型命名一个短的名字。 它只和 XML 配置有关, 只用来减少类完全限定名的多余部分。
自定义别名:
<!-- 配置别名 -->
<typeAliases>
<typeAlias alias="User" type="com.bruceliu.bean.User" />
<!-- 定义扫描包 -->
<package name="com.bruceliu.bean" />
</typeAliases>
注意:
使用定义的别名是不区分大小写的,但一般按java规则去使用即可,即user或者User
4.3 mappers
mapper映射文件的引入有3种方式:
- 路径相对于资源目录跟路径:
<mappers>
<mapper resource="com/bruceliu/dao/UserMapper.xml" />
</mappers>
- 使用完整的文件路径:
<mappers>
<mapper class="com.bruceliu.dao.UserMapper"/>
</mappers>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中
- 可直接配个扫描包:
<mappers>
<package name="com.bruceliu.dao"/>
</mappers>
五 Mapper XML 文件
Mapper映射文件是在实际开发过程中使用最多的,也是我们学习的重点。
Mapper文件中包含的元素有:
- cache – 配置给定命名空间的缓存。
- cache-ref – 从其他命名空间引用缓存配置。
- resultMap – 映射复杂的结果对象。
- sql – 可以重用的 SQL 块,也可以被其他语句引用。
- insert – 映射插入语句
- update – 映射更新语句
- delete – 映射删除语句
- select – 映射查询语句
5.1 CRUD
5.1.1 select
<select id="getById" parameterType="int" resultType="User">
select * from user where id=#{id}
</select>
select标签叫Statement
id,必要属性,在当前的命名空间下不能重复。
指定输出类型,resultType
parameterType (不是必须)如果不指定,自动识别。
5.1.2 insert
<!-- 增删改返回的都是int值 不用写返回值 -->
<insert id="addUser">
INSERT INTO USER VALUES (null,#{username},#{birthday},#{sex},#{address})
</insert>
insert标签叫Statement
id,必要属性,在当前的命名空间下不能重复。
parameterType (不是必须)如果不指定,自动识别。
5.1.3 如何获得到自增id (重要)**
<!-- 增删改返回的都是int值 不用写返回值 -->
<insert id="addUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO USER VALUES (null,#{username},#{birthday},#{sex},#{address})
</insert>
useGeneratedKeys:开启自增长映射
keyProperty:指定id所对应对象中的属性名
当执行完saveUser()方法后,其返回值依然是执行sql影响的行数,并不是要获取的自增ID!Mybatis会自动将返回的主键值赋值给对象User的属性id,因此你可以通过属性的get方法获得插入的主键值: System.out.println(User.getId());
-
另外一种写法
在插入操作完成之前或之后,可以配置标签获得生成的主键的值,获得插入之前还是之后的值,可以通过配置order属性来指定。
**LAST_INSERT_ID**:该函数是mysql的函数,获取自增主键的ID,它必须配合insert语句一起使用
<!-- 增删改返回的都是int值 不用写返回值 -->
<insert id="addUser" parameterType="User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO USER VALUES (null,#{username},#{birthday},#{sex},#{address})
</insert>
5.1.4 update
<update id="updateUser" >
update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
</update>
update标签叫Statement
id,必要属性,在当前的命名空间下不能重复。
parameterType (不是必须)如果不指定,自动识别。
5.1.5 删除 delete
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id}
</delete>
delete标签叫Statement
id,必要属性,在当前的命名空间下不能重复。
parameterType (不是必须)如果不指定,自动识别。
六 别名TypeAliases
当我们的bean对象较多的时候或者包名较长的时候,在传递类型或者返回类型中需要重复写类的全路径,这时可以使用mybatis的别名机制来简化书写,mybatis的别名使用主要包括以下三种情况:
1.系统自带别名
我们只需要输入前面的简写就可以对应到后面的数据类型,这是mybatis框架自带的别名,如果我们想自己编写自己的别名也可以通过下面的方式来完成
2.配置文件中设置
在配置文件中添加标签,并在里面配置类型别名
这里的type是你的bean的类型,alias指的是别名,配置好别名在mapper.xml中就可以直接使用别名了
测试:
结果:
但是,随着bean的增加,会不断的向里面添加,这时又有了一套更好的方法
3.配置pacakge
通过配置packge,在mapper.xml中通过类名引用
<typeAliases>
<package name="com.dream.bean"/>
</typeAliases>
测试代码:
int pageIndex=2;
int pageSize=3;
Map<String, Object> map=new HashMap<>();
map.put("pageStart", (pageIndex-1)*pageSize);
map.put("pageSize", pageSize);
List<Customer> page = session.selectList("page",map);
System.out.println(page);
mapper.xml
<select id="page" resultType="Customer" parameterType="map">
select * from customer limit #{pageStart},#{pageSize}
</select>
测试结果:
七 #和$区别(面试题)
7.1介绍
在映射文件配置<select>标签执行查询操作。
注意:
\- {}:相当于占位符
{id}:其中的id可以表示输入参数的名称,如果是简单类型名 称可以任意
\- **$**{}:表示拼接sql语句
\- **$**{value}:表示输入参数的名称,如果参数是简单类型,参 数名称必须是value
<!--
根据id查询用户,User findById(int id)
select:配置查询语句
id:可以通过id找到执行的statement,statement唯一标识
parameterType:输入参数类型
resultType:输出结果类型
#{}:相当于占位符
#{id}:其中的id可以表示输入参数的名称,如果是简单类型名称可以任意
-->
<select id="getById" parameterType="int" resultType="User" >
select * from user where id=#{id}
</select>
<!--
根据用户名称来模糊查询用户信息列表;
${}:表示拼接sql语句
${value}:表示输入参数的名称,如果参数是简单类型,参数名称必须是value
-->
<select id="findByUsername" parameterType="java.lang.String"
resultType="User">
select * from user where username like '%${value}%'
</select>
在Mybatis的mapper中,参数传递有2种方式,一种是#{}另一种是${},两者有着很大的区别:
#{} 实现的是sql语句的预处理参数,之后执行sql中用?号代替,使用时不需要关注数据类型,Mybatis自动实现数据类型的转换。并且可以防止SQL注入。 preparestament
${} 实现是sql语句的直接拼接,不做数据类型转换,需要自行判断数据类型。不能防止SQL注入。
7.2 总结
- #{} 占位符,用于参数传递。?占位
- ${}用于SQL拼接。 直接拼接在SQL语句中 有注入问题!如果是字符串 value
- 可以用statement和preparedstatement来做类比
八 parameterType的传入参数
传入类型有三种:
1、简单类型,string、long、integer等
2、Pojo类型,User等
3、HashMap类型。
8.1 传入参数是HashMap类型
查询需求:
<select id="getUsers" parameterType="map" resultType="User">
select * from user where birthday between #{startdate} and #{enddate}
</select>
查询测试:
@Test
public void test7(){
HashMap<String, Object> map=new HashMap<String, Object>();
map.put("startdate", "2018-09-07");
map.put("enddate", "2018-09-25");
List<User> users = mapper.getUsers(map);
for (User user : users) {
System.out.println(user);
}
}
注意:map的key要和sql中的占位符保持名字一致
8.2 分页
不允许在 关键字前后进行数学运算,需要在代码中计算完成后在传进去
java中代码
int pageIndex=2;
int pageSize=3;
Map<String, Object> map=new HashMap<>();
map.put("pageStart", (pageIndex-1)*pageSize);
map.put("pageSize", pageSize);
List<Customer> page = session.selectList("page",map);
mapper.xml
<select id="page" resultType="com.dream.bean.Customer" parameterType="map">
select * from customer limit #{pageStart},#{pageSize}
</select>
8.2.1使用注解
注意:mapper文件中的参数占位符的名字一定要和接口中参数的注解保持一致
mapper:
<!-- 分页:map传参 -->
<select id="selectUserByPage2" resultType="User">
SELECT * FROM USER LIMIT #{offset}, #{pagesize}
</select>
接口:
/**
* 根据分页参数查询
* @param offset 偏移量
* @param pagesize 每页条数
* @return 分页后的用户列表
*/
List<User> selectUserByPage2(@Param(value = "offset") int offset, @Param(value = "pagesize") int pagesize);
测试
@Test
public void testSelectAuthorByPage3() {
List<User> users = mapper.selectUserByPage3(1, 1);
for (int i = 0; i < users.size(); i++) {
System.out.println(users.get(i));
System.out.println("----------------------");
}
}
九 .返回Map类型查询结果
Mybatis中查询结果集为Map的功能,只需要重写ResultHandler接口,,然后用SqlSession 的select方法,将xml里面的映射文件的返回值配置成 HashMap 就可以了。具体过程如下
9.1 xml文件配置
<resultMap id="resultMap1" type="HashMap">
<result property="AA" column="r1" />
<result property="BB" column="r2" />
</resultMap>
<select id="getResult" resultMap="resultMap1">
select count(*) r1, max(birthday) r2 from user
</select>
接口:
测试
返回多个值
<select id="getResult" resultType="map">
select count(*) r1, max(birthday) r2,min(id) r3 from user
</select>
十 解决数据库字段和实体类属性不同
在平时的开发中,我们表中的字段名和表对应实体类的属性名称不一定都是完全相同的,下面来演示一下这种情况下的如何解决字段名与实体类属性名不相同的冲突。
上面的测试代码演示当实体类中的属性名和表中的字段名不一致时,使用MyBatis进行查询操作时无法查询出相应的结果的问题以及针对问题采用的两种办法:
解决办法一: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致,这样就可以表的字段名和实体类的属性名一一对应上了,这种方式是通过在sql语句中定义别名来解决字段名和属性名的映射关系的。
解决办法二: 通过来映射字段名和实体类属性名的一一对应关系。这种方式是使用MyBatis提供的解决方式来解决字段名和属性名的映射关系的。
十一 MyBatis整体架构
Mybatis是一个类似于Hibernate的**ORM****持久化框架**,支持普通SQL查询,存储过程以及高级映射。Mybatis通过使用简单的**XML**或**注解**用于配置和原始映射,将接口和POJO对象映射成数据库中的记录。
由于Mybatis是直接基于JDBC做了简单的映射包装,所有从性能角度来看:
JDBC > **Mybatis** > Hibernate
1、配置2类配置文件,其中一类是:Mybatis-Config.xml (名字不是写死,随便定义),另一类:Mapper.xml(多个),定义了sql片段;
2、通过配置文件得到SqlSessionFactory
3、通过SqlSessionFactory得到SqlSession(操作数据库)
4、通过底层的Executor(执行器)执行sql,Mybatis提供了2种实现,一种是基本实现,另一种带有缓存功能的实现;