Mybatis

Mybatis

1.Mybatis 介绍

MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及对结果集的检索封装。MyBatis 可以使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJO(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录. 半自动化框架。必须写sql语句。

2. mybatis 快速入门

编写第一个基于 mybaits 的测试例子:

2.1. 添加 jar 包

【mybatis】
mybatis-3.1.1.jar
【MYSQL 驱动包】
mysql-connector-java-5.1.7-bin.jar

2.2. 建库+表

create database mybatis;
use mybatis;
CREATE TABLE users(id INT PRIMARY KEY AUTO_INCREMENT, NAME
VARCHAR(20), age INT);
INSERT INTO users(NAME, age) VALUES('Tom', 12);
INSERT INTO users(NAME, age) VALUES('Jack', 11);

2.3. 添加 Mybatis 的配置文件conf.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">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
</configuration>

2.4. 定义表所对应的实体类

public class User {
private int id;
private String name;
private int age;
//get,set 方法
}

2.5. 定义操作 users 表的 sql 映射文件 userMapper.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.ykq.mybatis_test.test1.userMapper">
<select id="getUser" parameterType="int" resultType="com.ykq.mybatis_test.test1.User">
select * from users where id=#{id}
</select>
</mapper>

2.6. 在 conf.xml 文件中注册 userMapper.xml 文件

<mappers>
<mapper resource="com/ykq/mybatis_test/test1/userMapper.xml"/>
</mappers>


2.7. 编写测试代码:执行定义的 select 语句

public class Test {
public static void main(String[] args) throws IOException {
String resource = "conf.xml";
//加载 mybatis 的配置文件(它也加载关联的映射文件)
Reader reader = Resources.getResourceAsReader(resource);
//构建 sqlSession 的工厂
SqlSessionFactory sessionFactory = new
SqlSessionFactoryBuilder().build(reader);
//创建能执行映射文件中 sql 的 sqlSession
SqlSession session = sessionFactory.openSession();
//映射 sql 的标识字符串
String statement = "com.atguigu.mybatis.bean.userMapper"+".selectUser";
//执行查询返回一个唯一 user 对象的 sql
User user = session.selectOne(statement, 1);
System.out.println(user);
}

3. 操作 users 表的 CRUD

3.1. XML 的实现

1). 定义 sql 映射 xml 文件:

<insert id="insertUser" parameterType="com.ykq.ibatis.bean.User">
insert into users(name, age) values(#{name}, #{age});
</insert>
<delete id="deleteUser" parameterType="int">
delete from users where id=#{id}
</delete>
<update id="updateUser" parameterType="com.ykq.ibatis.bean.User">
update users set name=#{name},age=#{age} where id=#{id}
</update>
<select id="selectUser" parameterType="int" resultType="com.cd.ibatis.bean.User">
select * from users where id=#{id}
</select>
<select id="selectAllUsers" resultType="com.cd.ibatis.bean.User">
select * from users
</select>

2). 在 config.xml 中注册这个映射文件

<mappers>
<mapper resource="com/cd/mybatis_test/test1/userMapper.xml"/>
</mappers>

3). 在 dao 中调用:

User selectAllUsers();
User selectUser(int id);
int insertUser(User user);
int deleteUser(int id);
int updateUser(User user);

4. 实际开发的模式

接口结合映射文件一起用。

1.创建一个接口

package com.cd.dao;

import com.cd.entity.Users;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserDao {
    /**
     * 查询所有的用户信息
     * @return 所有的用户信息
     */
    public List<Users> selectAll();

    /**
     * 根据id查询用户信息
     * @param
     * @return
     */
    //@Param:表示把该参数的名称作为映射文件的参数名
    public Users selectByNameAndAge(@Param("name") String name,@Param("age") int age);
}

(2)创建映射文件

<?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 namespace="com.ykq.dao.UserDao">

    <!--id名称必须和接口中方法名一样-->
    <select id="selectAll"  resultType="com.ykq.entity.Users">
         select * from users
    </select>

    <select id="selectByNameAndAge" resultType="com.ykq.entity.Users">
         select * from users where name=#{name} and age=#{age}
    </select>

</mapper>

4. 几个可以优化的地方

4.1. 连接数据库的配置单独放在一个 properties 文件中

`## db.properties
<properties resource="db.properties"/>
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />`

4.2. 为实体类定义别名,简化 sql 映射 xml 文件中的引用

<typeAliases>
<typeAlias type="com.ykq.ibatis.bean.User" alias="_User"/>
</typeAliases>

4.3. 可以在 src 下加入 log4j 的配置文件打印日志信息

1. 添加 jar:
log4j-1.2.16.jar
2.1. log4j.properties(方式一)
log4j.properties,
log4j.rootLogger=DEBUG, Console
#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
2.2. log4j.xml(方式二)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="debug" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>

5. 解决字段名与实体类属性名不相同的冲突

5.1. 准备表和数据:

CREATE TABLE orders(
order_id INT PRIMARY KEY AUTO_INCREMENT,
order_no VARCHAR(20),
order_price FLOAT
);
INSERT INTO orders(order_no, order_price) VALUES('aaaa', 23);
INSERT INTO orders(order_no, order_price) VALUES('bbbb', 33);
INSERT INTO orders(order_no, order_price) VALUES('cccc', 22);

5.2. 定义实体类:

public class Order {
private int id;
private String orderNo;
private float price;
}

5.3. 实现 getOrderById(id)的查询:

方式一: 通过在 sql 语句中定义别名

<select id="selectOrder" parameterType="int" resultType="_Order">
select order_id id, order_no orderNo,order_price price from orders where
order_id=#{id}
</select>

方式二: 通过

<resultMap><select id="selectOrderResultMap" parameterType="int" resultMap="orderResultMap">
select * from orders where order_id=#{id}
</select>
<resultMap type="_Order" id="orderResultMap">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="price" column="order_price"/>
</resultMap>

6.实现关联表查询

6.1. 一对一关联

根据班级 id 查询班级信息(带老师的信息)

1). 创建表和数据

CREATE TABLE teacher(
t_id INT PRIMARY KEY AUTO_INCREMENT,
t_name VARCHAR(20)
);
CREATE TABLE class(
c_id INT PRIMARY KEY AUTO_INCREMENT,
c_name VARCHAR(20),
teacher_id INT
);
ALTER TABLE class ADD CONSTRAINT fk_teacher_id FOREIGN KEY (teacher_id)
REFERENCES teacher(t_id);
INSERT INTO teacher(t_name) VALUES('LS1');
INSERT INTO teacher(t_name) VALUES('LS2');
INSERT INTO class(c_name, teacher_id) VALUES('bj_a', 1);
INSERT INTO class(c_name, teacher_id) VALUES('bj_b', 2);

2). 定义实体类:

public class Teacher {
private int id;
private String name;
}
public class Classes {
private int id;
private String name;
private Teacher teacher;
}

3). 定义 sql 映射文件 ClassMapper.xml

<!--
方式一:嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集
封装联表查询的数据(去除重复的数据)
select * from class c, teacher t where c.teacher_id=t.t_id and c.c_id=1
-->
<select id="getClass" parameterType="int" resultMap="ClassResultMap">
select * from class c, teacher t where c.teacher_id=t.t_id and c.c_id=#{id}
</select>
<resultMap type="_Classes" id="ClassResultMap">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" javaType="_Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>
<!--
方式二:嵌套查询:通过执行另外一个 SQL 映射语句来返回预期的复杂类型
SELECT * FROM class WHERE c_id=1;
SELECT * FROM teacher WHERE t_id=1 //1 是上一个查询得到的 teacher_id 的值
-->
<select id="getClass2" parameterType="int" resultMap="ClassResultMap2">
select * from class where c_id=#{id}
</select>
<resultMap type="_Classes" id="ClassResultMap2">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" javaType="_Teacher"
select="getTeacher">
</association>
</resultMap>
<select id="getTeacher" parameterType="int" resultType="_Teacher">
SELECT t_id id, t_name name FROM teacher WHERE t_id=#{id}
</select>

4). 测试

@Test
public void testOO() {
SqlSession sqlSession = factory.openSession();
Classes c = sqlSession.selectOne("com.ykq.day03_mybatis.test5.OOMapper.getClass",
1);
System.out.println(c);
}
@Test
public void testOO2() {
SqlSession sqlSession = factory.openSession();
Classes c =
sqlSession.selectOne("com.ykq.day03_mybatis.test5.OOMapper.getClass2", 1);
System.out.println(c);
}

在这里插入图片描述

6.2. 一对多关联

1). 提出需求

根据 classId 查询对应的班级信息,包括学生,老师

2). 创建表和数据:

CREATE TABLE student(
s_id INT PRIMARY KEY AUTO_INCREMENT,
s_name VARCHAR(20),
class_id INT
);
INSERT INTO student(s_name, class_id) VALUES('xs_A', 1);
INSERT INTO student(s_name, class_id) VALUES('xs_B', 1);
INSERT INTO student(s_name, class_id) VALUES('xs_C', 1);
INSERT INTO student(s_name, class_id) VALUES('xs_D', 2);
INSERT INTO student(s_name, class_id) VALUES('xs_E', 2);
INSERT INTO student(s_name, class_id) VALUES('xs_F', 2);

3). 定义实体类:

public class Student {
private int id;
private String name;
}
public class Classes {
private int id;
private String name;
private Teacher teacher;
private List<Student> students;
}

4). 定义 sql 映射文件 ClassMapper.xml

<!--
方式一: 嵌套结果: 使用嵌套结果映射来处理重复的联合结果的子集
SELECT * FROM class c, teacher t,student s WHERE c.teacher_id=t.t_id AND c.C_id=s.class_id
AND c.c_id=1
-->
<select id="getClass3" parameterType="int" resultMap="ClassResultMap3">
select * from class c, teacher t,student s where c.teacher_id=t.t_id and c.C_id=s.class_id and
c.c_id=#{id}
</select>
<resultMap type="_Classes" id="ClassResultMap3">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" javaType="_Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
<!-- ofType 指定 students 集合中的对象类型 -->
<collection property="students" ofType="_Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>
<!--
方式二:嵌套查询:通过执行另外一个 SQL 映射语句来返回预期的复杂类型
SELECT * FROM class WHERE c_id=1;
SELECT * FROM teacher WHERE t_id=1 //1 是上一个查询得到的 teacher_id 的值
SELECT * FROM student WHERE class_id=1 //1 是第一个查询得到的 c_id 字段的值
-->
<select id="getClass4" parameterType="int" resultMap="ClassResultMap4">
select * from class where c_id=#{id}
</select>
<resultMap type="_Classes" id="ClassResultMap4">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" javaType="_Teacher"
select="getTeacher2"></association>
<collection property="students" ofType="_Student" column="c_id"
select="getStudent"></collection>
</resultMap>
<select id="getTeacher2" parameterType="int" resultType="_Teacher">
SELECT t_id id, t_name name FROM teacher WHERE t_id=#{id}
</select>
<select id="getStudent" parameterType="int" resultType="_Student">
SELECT s_id id, s_name name FROM student WHERE class_id=#{id}
</select>

5). 测试:

@Test
public void testOM() {
SqlSession sqlSession = factory.openSession();
Classes c = sqlSession.selectOne("com.ykq.day03_mybatis.test5.OOMapper.getClass3",
1);
System.out.println(c);
}
@Test
public void testOM2() {
SqlSession sqlSession = factory.openSession();
Classes c = sqlSession.selectOne("com.ykq.day03_mybatis.test5.OOMapper.getClass4",
1);
System.out.println(c);
}

在这里插入图片描述

7. 动态 SQL 与模糊查询

7.1 MyBatis动态语句分为4种元素:

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 **文章导出** ,生成一个.md文件或者.html文件进行本地保存。

7.2 Mybatis动态sql语句使用方式、例子

DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `money` double DEFAULT NULL,
  `isdeleted` tinyint(4) DEFAULT NULL,
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
)

1、if元素,如下

<select id="selByName" resultType="yuan.yuanmybatis.entity.Account">
       select id,name,created,updated from account where 1=1
       <if test="name !=null and name !=''">
           and name like concat('%',#{name},'%')
       </if>
</select>

2.choose

<select id="selByChoose" resultType="yuan.yuanmybatis.entity.Account">
        select id,name,created,updated,money from account where 1=1
        <choose>
            <when test="name !=null and name !=''">
                and name like concat('%',#{name},'%')
            </when>
            <when test="money !=null and money !=''">
                and money =#{money}
            </when>
            <otherwise>
                and isdeleted=1
            </otherwise>
        </choose>
    </select>

3.where元素,细心的读者会发现1、2点会有一个1=1,如果没有1=1,那么会变成如下错误语句:

select id,name,created,updated,money from account where and name like concat('%',#{name},'%')

这个时候我们可以用where元素处理sql:

<select id="selByName" resultType="yuan.yuanmybatis.entity.Account">
    select id,name,created,updated from account
    <where>
    <if test="name !=null and name !=''">
        and name like concat('%',#{name},'%')
    </if>
    </where>
    </select>

where元素在里面的条件成立的时候,才会加入where这个sql关键字

4.trim元素可以帮助我们去掉一下and、or等,prefix代表语句前缀, prefixOverrides代表要去掉的字符串

<select id="selByChoose" resultType="yuan.yuanmybatis.entity.Account">
        select id,name,created,updated,money from account
        <trim prefix="where" prefixOverrides="and">
        <choose>
            <when test="name !=null and name !=''">
                and name like concat('%',#{name},'%')
            </when>
            <when test="money !=null and money !=''">
                and money =#{money}
            </when>
            <otherwise>
                and isdeleted=1
            </otherwise>
        </choose>
        </trim>
    </select>

5.set元素,它可以在遇到逗号的时候,把对应的逗号去掉

<update id="updateAccout" parameterType="yuan.yuanmybatis.entity.Account">
        update account
        <set>
            <if test="name !=null and name !=''">
               name=#{name},
            </if>
            <if test="money!=null and money!=''">
               money=#{money}
            </if>
        </set>
        where id=#{id}
    </update>

6.foreach元素,它时一个循环语句,作用时用来遍历集合,支持数组、List、Set接口集合

<select id="selIn" resultType="yuan.yuanmybatis.entity.Account">
        select id,name,created,updated from account where name in
        <foreach collection="nameList" index="index" item="name" open="(" separator="," close=")">
        #{name}
       </foreach>
   </select>

8. Mybatis 缓存

8.1 MyBatis 缓存理解

正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持

1. 一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当
Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空。
2. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在
于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。
3. 对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存 Namespaces)的进行
了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。

8.2. Mybatis 一级缓存

1) 提出需求:

根据 id 查询对应的用户记录对象

2). 准备数据库表和数据

CREATE TABLE c_user(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT
);
INSERT INTO c_user(NAME, age) VALUES('Tom', 12);
INSERT INTO c_user(NAME, age) VALUES('Jack', 11);

3). 创建表的实体类

public class User implements Serializable{
private int id;
private String name;
private int age;
}

4). userMapper.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.atguigu.mybatis.test8.userMapper">
<select id="getUser" parameterType="int" resultType="_CUser">
select * from c_user where id=#{id}
</select>
<update id="updateUser" parameterType="_CUser">
update c_user set
    name=#{name}, age=#{age} where id=#{id}
</update>
</mapper>

5). 测试:

/*
* 一级缓存: 也就 Session 级的缓存(默认开启)
*/
@Test
public void testCache1() {
SqlSession session = MybatisUtils.getSession();
String statement = "com.ykq.mybatis.test8.userMapper.getUser";
User user = session.selectOne(statement, 1);
System.out.println(user);
/*
* 一级缓存默认就会被使用
*/
/*
user = session.selectOne(statement, 1);
System.out.println(user);
*/
/*
1. 必须是同一个 Session,如果 session 对象已经 close()过了就不可能用了
*/
/*
session = MybatisUtils.getSession();
user = session.selectOne(statement, 1);
System.out.println(user);
*/
/*
2. 查询条件是一样的
*/
/*
user = session.selectOne(statement, 2);
System.out.println(user);
*/
    /*
3. 没有执行过 session.clearCache()清理缓存
*/
/*
session.clearCache();
user = session.selectOne(statement, 2);
System.out.println(user);
*/
/*
4. 没有执行过增删改的操作(这些操作都会清理缓存)
*/
/*
session.update("com.ykq.mybatis.test8.userMapper.updateUser",
new User(2, "user", 23));
user = session.selectOne(statement, 2);
System.out.println(user);
*/
}

9. pagehelper分页插件

9.1 使用 PageHelper

你只需要在 classpath 中包含 pagehelper-x.y.z.jar 和 jsqlparser-x.y.z.jar。

9.2 在 MyBatis 配置 xml 中配置拦截器插件

  1. 2 在 MyBatis 配置 xml 中配置拦截器插件
<!-- 
    plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
    properties?, settings?, 
    typeAliases?, typeHandlers?, 
    objectFactory?,objectWrapperFactory?, 
    plugins?, 
    environments?, databaseIdProvider?, mappers?
-->
<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
        <property name="param1" value="value1"/>
	</plugin>
</plugins>

9.3 使用PageInfo的用法

//获取第1页,10条内容,默认查询总数count
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectAll();
//用PageInfo对结果进行包装
PageInfo page = new PageInfo(list);
//测试PageInfo全部属性
//PageInfo包含了非常全面的分页属性
assertEquals(1, page.getPageNum());
assertEquals(10, page.getPageSize());
assertEquals(1, page.getStartRow());
assertEquals(10, page.getEndRow());
assertEquals(183, page.getTotal());
assertEquals(19, page.getPages());
assertEquals(1, page.getFirstPage());
assertEquals(8, page.getLastPage());
assertEquals(true, page.isFirstPage());
assertEquals(false, page.isLastPage());
assertEquals(false, page.isHasPreviousPage());
assertEquals(true, page.isHasNextPage());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值