一、理论
1、历史
1)MyBatis是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation 迁移到了Google Code,随着开发团队转投Google Code旗下, iBatis3.x 正式更名为MyBatis ,代码于2013年11月迁移到Github
2)iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。 iBatis 提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
3) 什么是持久层框架?
看到持久层—> 保存到磁盘的技术。
持久层的框架很多很多: jdbc—>jdbcTemplate—>mybatis\jpa\Hibernate.…..
2、简介
1) MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架
2) MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
3) MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录
4) Mybatis 是一个半自动的ORM(Object Relation Mapping)框架,Hibernate 完全的ORM
数据库 ——-映射关系——- java实体
因为mybatis 还需要编写sql语句才能操作数据库,Hibernate无需操作数据库,直接操作java代码即可实现增删改查 from User
3、为什么选择MyBatis?
越接近底层的技术,运行速度越快,编写的代码越困难
JDBC —-> Hibernate (封装的有点狠)
随着互联网的发展,我们要求编码速度要快,运行速度也要快,就选择了一个折中的方案—MyBatis
随着MyBatis越来越流行,开发人员觉得MyBatis也有一些编码上的问题—-MyBatis-Plus
4、如何下载?
https://github.com/mybatis/mybatis-3/
该网站是全球最大的代码托管网站,现在由于一些技术封锁的原因,国内支持不是特别好,但是国内也诞生了一个类似的网站—码云。
目前所有项目都使用mavan创建了,所以jar包我们也不需要下载了,直接上Maven坐标。
二、入门案例
mybatis编写方式有很多,很灵活,今天所有同学都以我这个为准。
第一步:新建一个java的maven项目。打包方式写成 jar
第二步:导入依赖的jar包
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
第三步:导入log4j的配置文件,因为我们将来想在控制台打印日志
在resources 文件夹下,放log4j.properties
关于Log4J的内容说明
五种日志级别 debug info warn error fatal 级别依次提高,级别越低,打印出来的日志越多
第四步:准备一些表 hqll 数据库中customer表
第五步:编写Customer对应的实体 POJO
package com.qfedu.pojo;
import java.util.Date;
public class Customer {
private int id;
private String name;
private String phone;
private String email;
private String level;
private Date createTime;
private Date endTime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", phone='" + phone + '\'' +
", email='" + email + '\'' +
", level='" + level + '\'' +
", createTime=" + createTime +
", endTime=" + endTime +
'}';
}
}
第六步:创建一个全局的配置文件(mybatis 有两种配置文件,一种是总配置,一种是小配置文件)
创建一个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">
<configuration>
<!--配置数据库连接的环境-->
<environments default="development">
<environment id="development">
<!--使用jdbc默认的事务-->
<transactionManager type="JDBC"></transactionManager>
<!--使用默认的数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/hqll"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
第七步:创建接口文件
com.qfedu.mapper包下,创建CustomerMapper.java 接口
package com.qfedu.mapper;
import com.qfedu.pojo.Customer;
public interface CustomerMapper {
public Customer getCustomerById(int id);
}
第八步:创建小配置文件— CustomerMapper.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">
<mapper namespace="com.qfedu.mapper.CustomerMapper">
<select id="getCustomerById" resultType="com.qfedu.pojo.Customer">
select * from customer where id=#{id}
</select>
</mapper>
第九步:将小配置文件,告知给主配置文件
第十步:测试
@Test
public void testSelect01() throws IOException {
//测试mybatis
String resource="SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sessionFactory.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
Customer customer = customerMapper.getCustomerById(14);
System.out.println(customer);
}
以上方案—mapper接口编程,讲究以下几点照样:
1、接口名称和xml的配置文件名称一致,路径一致。
2、CustomerMapper.xml中的namespace 必须是接口的全路径
3、sql 中的id 必须是接口的方法名
4、SQL中的resultType必须和方法名的返回值一致。
三、一些细节上的配置
1、如果配置了log4j 日志不输出
<settings>
<setting name=”logImpl” value=”log4j”/>
</settings>
发现:SqlMapConfig.xml中各种标签是有先后书写顺序的,一不留神就报错。
2、能不能给各个实体起别名?
<!--起别名-->
<typeAliases>
<!--给某个实体起别名-->
<!--<typeAlias type="com.qfedu.pojo.Customer" alias="customer"/>-->
<!--指定一个包,该包下的所有的实体全部起别名,别名就是自己的类名,不区分大小写-->
<!--
Customer customer
cUstomer
-->
<package name="com.qfedu.pojo"/>
</typeAliases>
mybatis自身也给一个数据类型起了别名,别名如下:
3、每次编写一个mapper .xml都需要在SqlMapConfig.xml中进行报备,能不能有种简写的方式
<mappers>
<!--告知主配置文件,我的配置文件在哪里-->
<!--指定一个配置文件-->
<!--<mapper resource="com/qfedu/mapper/CustomerMapper.xml"/>-->
<!--指定一个包名,该包名下所有的xxxxMapper.xml全部加载进来-->
<package name="com.qfedu.mapper"/>
</mappers>
4、之前我们使用jdbc.properties来配置数据源,现在能够还使用这种方式呢?
<properties resource=”db.properties”/>
<environments default=”development”>
<environment id=”development”>
<transactionManager type=”JDBC”></transactionManager>
<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>
总结一个比较全的SqlMapConfig.xml
标签中的顺序如下: properties < settings < typeAliases < environments < mappers
官网给出的比较全的顺序:
以上顺序不需要记忆,当你需要写的时候,如果报错,换个位置,直到不报错就可以了。
<?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>
<!--通过该标签,将db.properties文件给加载过来-->
<properties resource="db.properties"/>
<!--解决一些同学控制台日志不打印的问题-->
<settings>
<setting name="logImpl" value="log4j"/>
</settings>
<!--起别名-->
<typeAliases>
<!--给某个实体起别名-->
<!--<typeAlias type="com.qfedu.pojo.Customer" alias="customer"/>-->
<!--指定一个包,该包下的所有的实体全部起别名,别名就是自己的类名,不区分大小写-->
<!--
Customer customer
cUstomer
-->
<package name="com.qfedu.pojo"/>
</typeAliases>
<!--配置数据库连接的环境-->
<environments default="development">
<environment id="development">
<!--使用jdbc默认的事务-->
<transactionManager type="JDBC"></transactionManager>
<!--使用默认的数据库连接池-->
<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>
<mappers>
<!--告知主配置文件,我的配置文件在哪里-->
<!--指定一个配置文件-->
<!--<mapper resource="com/qfedu/mapper/CustomerMapper.xml"/>-->
<!--指定一个包名,该包名下所有的xxxxMapper.xml全部加载进来-->
<package name="com.qfedu.mapper"/>
</mappers>
</configuration>
四、通过MyBatis实现增删改查
1、查询
public interface CustomerMapper {
Customer getCustomerById(int id);
List<Customer> selectAll();
}
<select id="getCustomerById" resultType="customer">
select * from customer where id=#{id}
</select>
<select id="selectAll" resultType="customer">
select * from customer
</select>
2、新增
先创建一个接口
int saveCustomer(Customer customer);
编写xml配置文件
<insert id=”saveCustomer” parameterType=”customer”>
insert into customer(name,phone,email,level) values(#{name},#{phone},#{email},#{level})
</insert>
此接口,返回值是int类型,我们无需在xml中管这个返回值,默认就有的,修改和删除也是一样的。
3、修改
int updateCustomerById(Customer customer);
<update id=”updateCustomerById” parameterType=”customer”>
update customer set name=#{name},phone=#{phone},email=#{email},level=#{level}
where id=#{id}
</update>
4、删除
int deleteById(int id);
<delete id=”deleteById” parameterType=”int”>
delete from customer where id=#{id}
</delete>
总结:
1、新增 insert 修改 update 删除 使用delete 标签 查询使用 select 标签
2、新增,修改,删除都需要进行事务的提交才能更新数据。
3、新增,修改,删除 都默认会有一个int类型的返回值,如果想使用这个返回值只需要接口中返回值是int即可。
4、parameterType 就是接口中的参数,类型一定要一致
5、resultType 就是接口中的返回值类型,如果接口的返回值是List集合,此处不需要写成List,而是写list集合中泛型的类型
5、如何获取自增的id值
如果主键是自增的,我如何保存完数据之后就知道该数据在数据库中的id是多少呢?
如何在新增完数据之后,就能拿到新数据在数据库中的id值呢,需要在配置文件中多如下配置:
select last_insert_id()
原理是通过执行mysql 中的一个内置函数 last_insert_id() 查询到最后一个数据的id.
keyColumn 指的是数据库中的字段 id
keyProperty 指的是实体中的字段 id
resultType 返回值类型 int
order 只有两个值 BEFORE AFTER 因为id是数据保存之后才会得出一个的id,所有类型是After
int insertCustomer(Customer customer);
xml配置中多了一段配置:
<insert id="insertCustomer" parameterType="customer">
<selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
select last_insert_id()
</selectKey>
insert into customer(name,phone,email,level) values(#{name},#{phone},#{email},#{level})
</insert>
测试代码:
@Test
public void testInsert2() throws IOException {
SqlSession sqlSession = sessionFactory.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
Customer customer = new Customer();
customer.setName("老闫");
customer.setPhone("18137884406");
customer.setEmail("838700991@qq.com");
customer.setLevel("年卡会员");
int result = customerMapper.insertCustomer(customer);
System.out.println(result);
System.out.println("保存数据之后,新数据在数据库中的id="+customer.getId());
//记得提交事务,否则数据不保存
sqlSession.commit();
sqlSession.close();
}
五、查询数据时数据库中的字段和实体中的字段不一致,查询不出来,怎么解决?
Customer{id=14, name=’刘晓磊’, phone=’18634455554’, email=’343223ddddd2@qq.com’, level=’年卡用户’, createTime=null, endTime=null}
需要使用resultMap == 专门解决实体中的属性和数据库中的字段不一致的问题
<resultMap id="customerMap" type="customer">
<!--id 表示该列是一个主键-->
<id property="id" column="id"></id>
<result property="createTime" column="create_time"/>
<result property="endTime" column="end_time"/>
</resultMap>
1、resultMap中的id一般随便写,type就是实体的全路径,可以使用别名。
2、里面的标签 主键使用id标签,普通字段使用 result标签
3、标签里面的property 指的是实体中的属性,column指的是数据库中的字段。
4、只需要编写属性和列不一致的字段即可,无需都写出来
使用的时候,记得resultType 换成 resultMap即可
测试查询出来的结果如下:
Customer{id=14, name=’刘晓磊’, phone=’18634455554’, email=’343223ddddd2@qq.com’, level=’年卡用户’, createTime=Thu Sep 09 16:04:53 CST 2021, endTime=null}
如果插入数据出现乱码,记得添加如下参数: useUnicode=true&characterEncoding=UTF-8