MyBatis框架 什么是框架 框架即是一个半成品软件。
开发者从头开发一个软件需要花费大量精力,于是有一些项目组开发出半成品软件,开发者在这些软件的基础上进行开发,这样的软件就称之为框架。
如果将开发完成的软件比作是一套已经装修完毕的新房,框架就好比是一套已经修建好的毛坯房。用户直接购买毛坯房,保证建筑质量和户型合理的同时可以进行风格的自由装修
使用框架开发的好处:
省去大量的代码编写、减少开发时间、降低开发难度。
限制程序员必须使用框架规范开发,增强代码的规范性,降低程序员之间沟通及日后维护的成本。
将程序员的注意力从技术中抽离出来,更集中在业务层面。
什么是ORM框架? ORM(Object Relationl Mapping),对象关系映射,即在数据库和对象之间作映射处理。
之前我们使用JDBC操作数据库,必须手动进行数据库和对象间的数据转换。
public void AddUser (User user) throws Exception { Class.forName("com.mysql.jdbc.Driver" ); Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" ,"root" , "root" ); String sql = "INSERT INTO user values (null,?,?,?,?)" ; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1 ,user.getName()); preparedStatement.setInt(2 ,user.getAge()); preparedStatement.setString(3 ,user.getAddress()); preparedStatement.setString(4 ,user.getSex()); preparedStatement.executeUpdate(); } public List<User> findAllUser () throws Exception { Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" , "root" , "root" ); PreparedStatement preparedStatement = connection.prepareStatement("select * from user" ); ResultSet resultSet = preparedStatement.executeQuery(); List<User> users = new ArrayList <>(); while (resultSet.next()){ int id = resultSet.getInt("id" ); String name = resultSet.getString("name" ); int age = resultSet.getInt("age" ); String address = resultSet.getString("address" ); String sex = resultSet.getString("sex" ); User user = new User (); user.setId(id); user.setName(name); user.setAge(age); user.setAddress(address); user.setSex(sex); users.add(user); } return users;}
这段代码中,数据库数据与对象数据的转换代码繁琐、无技术含量。
而使用ORM框架代替JDBC后,框架可以帮助程序员自动进行转换,只要像平时一样操作对象,ORM框架就会根据映射完成对数据库的操作,极大的增强了开发效率。
什么是MyBatis?
MyBatis是一个半自动的ORM框架 ,其本质是对JDBC的封装。使用MyBatis不需要写JDBC代码,但需要程序员编写SQL语句 。之前是apache的一个开源项目iBatis,2010年改名为MyBatis。
补充: Hibernate也是一款持久层ORM框架,多年前的市场占有率很高,但近年来市场占有率越来越低。MyBatis与Hibernate的比较:
MyBatis是一个半自动的ORM框架,需要手写SQL语句。
Hibernate是一个全自动的ORM框架,不需要手写SQL语句。
使用MyBatis的开发量要大于Hibernate。
为什么Hibernate市场占有率越来越低:
对于新手学习Hibernate时间成本比MyBatis大很多,MyBatis上手很快。
Hibernate不需要写SQL语句是因为框架来生成SQL语句。对于复杂查询,开发者很难控制生成的SQL语句,这就导致SQL调优很难进行。
之前的项目功能简单,数据量小,所以使用Hibernate可以快速完成开发。而近年来项目的数据量越来越大,而互联网项目对查询速度要求也很高,这就要求我们一定要精细化的调整SQL语句。此时灵活性更强,手动编写SQL语句的MyBatis慢慢代替了Hibernate使用。
在高并发、大数据、高性能、高响应的互联网项目中,MyBatis是首选的持久框架 。而对于对性能要求不高的比如内部管理系统等可以使用Hibernate。
MyBatis入门
环境搭建:
将SQL文件导入数据库
创建maven工程,引入依赖
<?xml version="1.0" encoding="UTF-8" ?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.itbaizhan</groupId > <artifactId > mybatisDemo1</artifactId > <version > 1.0-SNAPSHOT</version > <dependencies > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.7</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.26</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.10</version > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.12</version > </dependency > </dependencies > </project >
3.创建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 ="mysql" > <environment id ="mysql" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql:///mybatis" /> <property name ="username" value ="root" /> <property name ="password" value ="root" /> </dataSource > </environment > </environments > </configuration >
4.将log4j.properties文件放入resources中,让控制台打印SQL语句
log4j.rootCategory =debug, CONSOLE log4j.appender.CONSOLE =org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout =org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern =[%d{MM/dd HH:mm:ss}] %-6r [%15.15t] %-5p %30.30c %x - %m\n
5.创建实体类
package com.itbaizhan.pojo;public class User { private int id; private String username; private String sex; private String address; public int getId () { return id; } public void setId (int 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 String getAddress () { return address; } public void setAddress (String address) { this .address = address; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}' ; } public User (int id, String username, String sex, String address) { this .id = id; this .username = username; this .sex = sex; this .address = address; } public User () { } }
创建持久层接口和映射文件
在java目录创建持久层接口
package com.itbaizhan.mapper;import com.itbaizhan.pojo.User;import java.util.List;public interface UserMapper { List<User> findAll () ; }
2.在resource目录创建映射文件
<?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.itbaizhan.mapper.UserMapper" > <select id ="findAll" resultType ="com.itbaizhan.pojo.User" > select * from user </select > </mapper >
3.将映射文件配置到mybatis核心配置文件中
<?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 ="mysql" > <environment id ="mysql" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql:///mybatis" /> <property name ="username" value ="root" /> <property name ="password" value ="root" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="com/itbaizhan/mapper/UserMapper.xml" > </mapper > </mappers > </configuration >
映射文件注意事项:
映射文件要和接口名称相同。
映射文件要和接口的目录结构相同。
映射文件中namespace属性要写接口的全名。
映射文件中标签的id属性是接口方法的方法名 。
映射文件中标签的resultType属性是接口方法的返回值类型 。
映射文件中标签的parameterType属性是接口方法的参数类型 。
映射文件中resultType、parameterType属性要写全类名,如果是集合类型,则写其泛型的全类名。
测试持久层接口方法
import com.itbaizhan.mapper.UserMapper;import com.itbaizhan.pojo.User;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.util.List;public class TestUserMapper { @Test public void testFindAll () throws IOException { InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = builder.build(is); SqlSession sesison = factory.openSession(); UserMapper mapper = sesison.getMapper(UserMapper.class); List<User> all = mapper.findAll(); for (User user : all) { System.out.println(user.toString()); } sesison.close(); is.close(); } }
MyBatis核心对象以及工作流程
MyBatis核心对象 SqlSessionFactoryBuilder
SqlSession工厂构建者对象,使用构造者模式创建SqlSession工厂对象。
SqlSessionFactory
SqlSession工厂,使用工厂模式创建SqlSession对象。
SqlSession
该对象可以操作数据库,也可以使用动态代理模式创建持久层接口的代理对象操作数据库。
Mapper
持久层接口的代理对象,他具体实现了持久层接口,用来操作数据库
MyBatis工作流程
创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder对象构建了SqlSessionFactory对象:构造者模式
SqlSessionFactory对象生产了SqlSession对象:工厂模式
SqlSession对象创建了持久层接口的代理对象:动态代理模式
代理对象操作数据库
使用SqlSession操作数据库
除了代理对象能够操作数据库,SqlSession也能操作数据库。只是这种方式在开发中使用的较少,接下来我们使用SqlSession操作数据库:
@Test public void testFindAll2 () throws IOException { InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = builder.build(is); SqlSession sesison = factory.openSession(); List<Object> users = sesison.selectList("com.itbaizhan.mapper.UserMapper.findAll" ); for (Object user : users) { System.out.println(user.toString()); } sesison.close(); is.close(); }
Mapper动态代理原理
接下来我们通过源码,了解MyBatis的Mapper对象究竟是怎么生成的,他又是如何代理接口的方法。
获取代理对象 点开测试类的getMapper
方法,查看该方法最终调用了什么方法。
当看到Proxy.newProxyInstance
时,可以确定getMapper
方法最终调用的是JDK动态代理方法,且使用MapperProxy类定义代理方式
查看代理方式 点开MapperProxy类,查看invoke方法,查看代理对象是如何工作的。
可以看到,MapperProxy调用了MapperMethod的execute方法定义了代理方式,且底层调用的是SqlSession的方法,根据映射文件标签不同调用不同的SqlSession方法。
结论:
SqlSession的getMapper方法,最终是调用的是JDK动态代理方法 ,生成一个代理对象,类型就是传入的接口类型。
MapperProxy对象通过调用MapperMethod的execute方法定义了代理方式,该方法的底层调用的是SqlSession的方法。
MyBatis新增 新增用户 1.持久层接口添加方法
package com.itbaizhan.mapper;import com.itbaizhan.pojo.User;import java.util.List;public interface UserMapper { List<User> findAll () ; void add (User user) ; }
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" > <mapper namespace ="com.itbaizhan.mapper.UserMapper" > <select id ="findAll" resultType ="com.itbaizhan.pojo.User" > select * from user </select > <insert id ="add" parameterType ="com.itbaizhan.pojo.User" > insert into user (username,sex,address) values (#{username},#{sex},#{address})) </insert > </mapper >
3.编写测试方法
@Test public void testAdd () throws Exception{ InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = builder.build(is); SqlSession session = factory.openSession(); UserMapper userMapper = session.getMapper(UserMapper.class); User user = new User ("程序员" , "男" , "上海" ); userMapper.add(user); session.commit(); session.close(); is.close(); }
注意: 1.当接口方法的参数类型为POJO类型时,SQL语句中绑定参数时使用#{POJO的属性名}即可
2.MyBatis事务默认手动提交,所以在执行完增删改方法后,需要手动调用SqlSession对象的事务提交方法,否则数据库将不发生改变。
MyBatis修改 优化测试类 我们发现MyBatis的测试方法在操作数据库前都需要获取代理对象,操作数据库后都需要释放资源,可以利用Junit的前置与后置方法,优化测试类代码。
public class TestUserMapper2 { InputStream is = null ; SqlSession session = null ; UserMapper userMapper = null ; @Before public void before () throws IOException { is = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = builder.build(is); session = factory.openSession(); userMapper = session.getMapper(UserMapper.class); } @After public void after () throws IOException { session.close(); is.close(); }
修改用户 1.持久层接口添加方法
package com.itbaizhan.mapper;import com.itbaizhan.pojo.User;import java.util.List;public interface UserMapper { List<User> findAll () ; void add (User user) ; void update (User user) ; }
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" > <mapper namespace ="com.itbaizhan.mapper.UserMapper" > <select id ="findAll" resultType ="com.itbaizhan.pojo.User" > select * from user </select > <insert id ="add" parameterType ="com.itbaizhan.pojo.User" > insert into user (username,sex,address) values (#{username},#{sex},#{address}) </insert > <update id ="update" parameterType ="com.itbaizhan.pojo.User" > update user set username = #{username},sex = #{sex},address=#{address} where id = #{id} update user set username = #{username},sex = #{sex},address=#{address} where id = #{id} </update > </mapper >
3.编写测试方法
@Test public void testUpdate () { User user = new User (1 ,"程序员1" ,"女" ,"深圳" ); userMapper.update(user); session.commit(); }
MyBatis删除、根据ID查询 删除用户
1.持久层接口添加方法
public interface UserMapper { List<User> findAll () ; void add (User user) ; void update (User user) ; void delete (int userId) ; }
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" > <mapper namespace ="com.itbaizhan.mapper.UserMapper" > <select id ="findAll" resultType ="com.itbaizhan.pojo.User" > select * from user </select > <insert id ="add" parameterType ="com.itbaizhan.pojo.User" > insert into user (username,sex,address) values (#{username},#{sex},#{address}) </insert > <update id ="update" parameterType ="com.itbaizhan.pojo.User" > update user set username = #{username},sex = #{sex},address=#{address} where id = #{id} </update > <delete id ="delete" parameterType ="int" > delete from user where id = #{id} delete from user where id = #{id} </delete > </mapper >
注意:
当方法的参数类型是简单数据类型时,#{}中可以写任意名称
3.编写测试方法
@Test public void testDelete () { userMapper.delete(7 ); session.commit(); }
根据ID查询用户 1.持久层接口添加方法
package com.itbaizhan.mapper;import com.itbaizhan.pojo.User;import java.util.List;public interface UserMapper { List<User> findAll () ; void add (User user) ; void update (User user) ; void delete (int userId) ; User findById (int userId) ; }
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" > <mapper namespace ="com.itbaizhan.mapper.UserMapper" > <select id ="findAll" resultType ="com.itbaizhan.pojo.User" > select * from user </select > <insert id ="add" parameterType ="com.itbaizhan.pojo.User" > insert into user (username,sex,address) values (#{username},#{sex},#{address}) </insert > <update id ="update" parameterType ="com.itbaizhan.pojo.User" > update user set username = #{username},sex = #{sex},address=#{address} where id = #{id} </update > <delete id ="delete" parameterType ="int" > delete from user where id = #{id} </delete > <select id ="findById" parameterType ="int" resultType ="com.itbaizhan.pojo.User" > select * from user where id = #{userid} select * from user where id = #{userid} </select > </mapper >
3.编写测试方法
@Test public void testFindById () { User user = userMapper.findById(1 ); System.out.println(user); }
MyBatis模糊查询 使用#定义参数 1.持久层接口添加方法
package com.itbaizhan.mapper;import com.itbaizhan.pojo.User;import java.util.List;public interface UserMapper { List<User> findAll () ; void add (User user) ; void update (User user) ; void delete (int userId) ; User findById (int userId) ; List<User> findByUsernameLike (String username) ; List<User> findByUsernameLike (String username) ; }
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" > <mapper namespace ="com.itbaizhan.mapper.UserMapper" > <select id ="findAll" resultType ="com.itbaizhan.pojo.User" > select * from user </select > <insert id ="add" parameterType ="com.itbaizhan.pojo.User" > insert into user (username,sex,address) values (#{username},#{sex},#{address}) </insert > <update id ="update" parameterType ="com.itbaizhan.pojo.User" > update user set username = #{username},sex = #{sex},address=#{address} where id = #{id} </update > <delete id ="delete" parameterType ="int" > delete from user where id = #{id} </delete > <select id ="findById" parameterType ="int" resultType ="com.itbaizhan.pojo.User" > select * from user where id = #{userid} </select > <select id ="findByUsernameLike" parameterType ="string" resultType ="com.itbaizhan.pojo.User" > select * from user where username like #{user} select * from user where username like #{user} </select > </mapper >
3.编写测试方法
@Test public void testFindByNameLike () { List<User> users = userMapper.findByUsernameLike("%尚学堂%" ); List<User> users = userMapper.findByUsernameLike("%尚学堂%" ); users.forEach(System.out::println); }
我们看到在映射文件中,parameterType的值为string而没有写java.lang.String,这是为什么呢?
参数/返回值类型为基本数据类型/包装类/String等类型时,我们可以写全类名,也可以写别名
数据类型
别名
byte
_byte
long
_long
short
_short
int
_int
int
_integer
double
_double
float
_float
boolean
_boolean
String
string
Byte
byte
Long
long
Short
short
Integer
int/integer
Double
double
Float
float
Boolean
boolean
Date
date
BigDecimal
decimal/bigdecimal
Object
object
Map
map
HashMap
hashmap
List
list
ArrayList
arraylist
Collection
collection
Iterator
iterator
使用$定义参数 模糊查询如果不想在调用方法时参数加%,可以使用拼接参数的方式设置Sql:
<select id ="findByUsernameLike" parameterType ="string" resultType ="com.itbaizhan.pojo.User" > select * from user where username like '%${value}%' select * from user where username like '%${value}%' </select >
测试方法写法如下:
@Test public void testFindByNameLike () { List<User> users = userMapper.findByUsernameLike("尚学堂" ); users.forEach(System.out::println); }
#和$的区别:
#表示sql模板的占位符,$表示将字符串拼接到sql模板中。
#可以防止sql注入,一般能用#就不用$。
#可以防止sql注入,一般能用#就不用$
${}内部的参数名必须写value。
使用定义参数 如果使用#
还不想在调用方法的参数中添加%
,可以使用<bind>
,<bind>
允许我们在 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" > <mapper namespace ="com.itbaizhan.mapper.UserMapper" > <select id ="findAll" resultType ="com.itbaizhan.pojo.User" > select * from user </select > <insert id ="add" parameterType ="com.itbaizhan.pojo.User" > insert into user (username,sex,address) values (#{username},#{sex},#{address}) </insert > <update id ="update" parameterType ="com.itbaizhan.pojo.User" > update user set username = #{username},sex = #{sex},address=#{address} where id = #{id} </update > <delete id ="delete" parameterType ="int" > delete from user where id = #{id} </delete > <select id ="findById" parameterType ="int" resultType ="com.itbaizhan.pojo.User" > select * from user where id = #{userid} </select > <select id ="findByUsernameLike" parameterType ="string" resultType ="com.itbaizhan.pojo.User" > <bind name ="likeName" value ="'%'+username+'%'" /> <bind name ="likeName" value ="'%'+username+'%'" > select * from user where username like #{likeName} select * from user where username like #{likeName} </select > </mapper >
测试方法写法如下;
@Test public void testFindByNameLike () { List<User> users = userMapper.findByUsernameLike("尚学堂" ); users.forEach(System.out::println); }
MyBatis分页查询
分页查询时,Sql语句使用limit关键字,需要传入开始索引和每页条数两个参数。MyBatis的多参数处理有以下方式:
顺序传参: Sql中的参数使用arg0,arg1…或param1,param2…表示参数的顺序。此方法可读性较低,在开发中不建议使用。
1.持久层接口方法
package com.itbaizhan.mapper;import com.itbaizhan.pojo.User;import org.apache.ibatis.annotations.Param;import java.util.List;public interface UserMapper { List<User> findAll () ; void add (User user) ; void update (User user) ; void delete (int userId) ; User findById (int userId) ; List<User> findByUsernameLike (String username) ; List<User> findPage (int startIndex,int pageSize) ; List<User> findPage (int startIndex,int pageSize) ; }
2.映射文件
<select id ="findPage" resultType ="com.itbaizhan.pojo.User" > select * from user limit #{arg0},#{arg1} </select > <select id ="findPage" resultType ="com.itbaizhan.pojo.User" > select * from user limit #{param1},#{param2} selecy * from user limit #{param1},#{param2} </select >
3.测试类
@Test public void testFindPage () { List<User> users = userMapper.findPage(0 ,3 ); users.forEach(System.out::println); }
@Param传参 在接口方法的参数列表中通过@Param定义参数名称,在Sql语句中通过注解中所定义的参数名称指定参数位置。此方式参数比较直观的,推荐使用。
1.持久层接口方法
package com.itbaizhan.mapper;import com.itbaizhan.pojo.User;import org.apache.ibatis.annotations.Param;import java.util.List;public interface UserMapper { List<User> findAll () ; void add (User user) ; void update (User user) ; void delete (int userId) ; User findById (int userId) ; List<User> findByUsernameLike (String username) ; List<User> findPage (int startIndex,int pageSize) ; List<User> findPage1 (@Param("startIndex") int startIndex, @Param("pageSize") int pageSize) ; List<User> findPage1 (@Param("startIndex") int startIndex, @Param("pageSize") int pageSize) ; }
2.映射文件
<select id ="findPage1" resultType ="com.itbaizhan.pojo.User" > select * from user limit #{startIndex},#{pageSize} select * from user limit #{startIndex},#{pageSize} </select >
3.测试类
@Test public void testFindPage1 () { List<User> users = userMapper.findPage1(3 ,3 ); users.forEach(System.out::println); }
MyBatis映射文件的Sql语句中表示接口方法的第一个参数可以写成:
#{arg0}或#{param1}
MyBatis接口方法的参数前使用,定义映射文件中Sql语句的参数名:
@Param
POJO传参 自定义POJO类,该类的属性就是要传递的参数,在SQL语句中绑定参数时使用POJO的属性名作为参数名即可。此方式推荐使用。
1.自定义POJO类
package com.itbaizhan.pojo;public class PageQuery { private int startIndex; private int pageSize; public int getStartIndex () { return startIndex; } public void setStartIndex (int startIndex) { this .startIndex = startIndex; } public int getPageSize () { return pageSize; } public void setPageSize (int pageSize) { this .pageSize = pageSize; } public PageQuery () { } public PageQuery (int startIndex, int pageSize) { this .startIndex = startIndex; this .pageSize = pageSize; } }
2.持久层接口方法
package com.itbaizhan.mapper;import com.itbaizhan.pojo.PageQuery;import com.itbaizhan.pojo.User;import org.apache.ibatis.annotations.Param;import java.util.List;import java.util.Map;public interface UserMapper { List<User> findAll () ; void add (User user) ; void update (User user) ; void delete (int userId) ; User findById (int userId) ; List<User> findByUsernameLike (String username) ; List<User> findPage (int startIndex,int pageSize) ; List<User> findPage1 (@Param("startIndex") int startIndex, @Param("pageSize") int pageSize) ; List<User> findPage2 (PageQuery pageQuery) ; }
3.映射文件
<select id ="findPage2" resultType ="com.itbaizhan.pojo.User" parameterType ="com.itbaizhan.pojo.PageQuery" > 2 select * from user limit #{startIndex},#{pageSize} 3 </select >
4.测试类
@Test public void testFindPage2 () { PageQuery pageQuery = new PageQuery (3 , 3 ); List<User> users = userMapper.findPage2(pageQuery); users.forEach(System.out::println); }
Map传参 如果不想自定义POJO,可以使用Map作为传递参数的载体,在SQL语句中绑定参数时使用Map的Key作为参数名即可。此方法推荐使用。
1.持久层接口方法
package com.itbaizhan.mapper;import com.itbaizhan.pojo.PageQuery;import com.itbaizhan.pojo.User;import org.apache.ibatis.annotations.Param;import java.util.List;import java.util.Map;public interface UserMapper { List<User> findAll () ; void add (User user) ; void update (User user) ; void delete (int userId) ; User findById (int userId) ; List<User> findByUsernameLike (String username) ; List<User> findPage (int startIndex,int pageSize) ; List<User> findPage1 (@Param("startIndex") int startIndex, @Param("pageSize") int pageSize) ; List<User> findPage2 (PageQuery pageQuery) ; List<User> findPage3 (Map<String,Object> params) ; List<User> findPage3 (Map<String,Object> params) ; }
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" > <mapper namespace ="com.itbaizhan.mapper.UserMapper" > <select id ="findAll" resultType ="com.itbaizhan.pojo.User" > select * from user </select > <insert id ="add" parameterType ="com.itbaizhan.pojo.User" > insert into user (username,sex,address) values (#{username},#{sex},#{address}) </insert > <update id ="update" parameterType ="com.itbaizhan.pojo.User" > update user set username = #{username},sex = #{sex},address=#{address} where id = #{id} </update > <delete id ="delete" parameterType ="int" > delete from user where id = #{id} </delete > <select id ="findById" parameterType ="int" resultType ="com.itbaizhan.pojo.User" > select * from user where id = #{userid} </select > <select id ="findByUsernameLike" parameterType ="string" resultType ="com.itbaizhan.pojo.User" > <bind name ="likeName" value ="'%'+username+'%'" /> select * from user where username like #{likeName} </select > <select id ="findPage" resultType ="com.itbaizhan.pojo.User" > select * from user limit #{param1},#{param2} </select > <select id ="findPage1" resultType ="com.itbaizhan.pojo.User" > select * from user limit #{startIndex},#{pageSize} </select > <select id ="findPage2" resultType ="com.itbaizhan.pojo.User" parameterType ="com.itbaizhan.pojo.PageQuery" > select * from user limit #{startIndex},#{pageSize} </select > <select id ="findPage3" resultType ="com.itbaizhan.pojo.User" parameterType ="map" > select * from user limit #{startIndex},#{pageSize} </select > </mapper >
3.测试类
@Test public void testFindPage3 () { Map<String,Object> params = new HashMap <>(); params.put("startIndex" ,0 ); params.put("pageSize" ,4 ); List<User> users = userMapper.findPage3(params); users.forEach(System.out::println); }
在MyBatis中,能作为参数载体的是:
Map
MyBatis聚合查询、主键回填 查询用户总数 1.持久层方法
2.映射文件
<select id ="findCount" resultType ="int" > select count(id) from user </select >
3.测试类
@Test public void testFindCount () { int count = userMapper.findCount(); System.out.println(count); }
主键回填 有时我们需要获取新插入数据的主键值。如果数据库中主键是自增的,这时我们就需要使用MyBatis的主键回填功能。
1.持久层接口方法
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" > <mapper namespace ="com.itbaizhan.mapper.UserMapper" > <select id ="findAll" resultType ="com.itbaizhan.pojo.User" > select * from user </select > <insert id ="add" parameterType ="com.itbaizhan.pojo.User" > insert into user (username,sex,address) values (#{username},#{sex},#{address}) </insert > <update id ="update" parameterType ="com.itbaizhan.pojo.User" > update user set username = #{username},sex = #{sex},address=#{address} where id = #{id} </update > <delete id ="delete" parameterType ="int" > delete from user where id = #{id} </delete > <select id ="findById" parameterType ="int" resultType ="com.itbaizhan.pojo.User" > select * from user where id = #{userid} </select > <select id ="findByUsernameLike" parameterType ="string" resultType ="com.itbaizhan.pojo.User" > <bind name ="likeName" value ="'%'+username+'%'" /> select * from user where username like #{likeName} </select > <select id ="findPage" resultType ="com.itbaizhan.pojo.User" > select * from user limit #{param1},#{param2} </select > <select id ="findPage1" resultType ="com.itbaizhan.pojo.User" > select * from user limit #{startIndex},#{pageSize} </select > <select id ="findPage2" resultType ="com.itbaizhan.pojo.User" parameterType ="com.itbaizhan.pojo.PageQuery" > select * from user limit #{startIndex},#{pageSize} </select > <select id ="findPage3" resultType ="com.itbaizhan.pojo.User" parameterType ="map" > select * from user limit #{startIndex},#{pageSize} </select > <select id ="findCount" resultType ="int" > select count(id) from user </select > <insert id ="add2" parameterType ="com.itbaizhan.pojo.User" > <selectKey keyProperty ="id" keyColumn ="id" resultType ="int" order ="AFTER" > SELECT LAST_INSERT_ID(); //SELECT LAST_INSERT_ID(); </selectKey > insert into user(username, sex, address) VALUES (#{username},#{sex},#{address}) </insert > <insert id ="add2" parameterType ="com.itbaizhan.pojo.User" > <selectKey keyProperty ="id" keyColumn ="id" resultType ="int" order ="AFTER" > SELECT LAST_INSERT_ID(); </selectKey > insert into user(username,sex,address) VALUES(#{username},#{sex},#{address}) </insert > </mapper >
SELECT LAST_INSERT_ID():查询刚刚插入的记录的主键值,只适用于自增主键,且必须和insert语句一起执行。
3.测试类
@Test public void testAdd2 () { User user = new User ("程序猿" , "男" , "北京" ); userMapper.add2(user); session.commit(); System.out.println(user); }
MyBatis配置文件_
MyBatis配置文件结构:
-configuration -properties(属性) -property -settings(全局配置参数) -setting -plugins(插件) -plugin -typeAliases(别名) -typeAliase -package -environments(环境) -environment -transactionManager(事务管理) -dataSource(数据源) -mappers(映射器) -mapper -package
properties 属性值定义。properties标签中可以定义属性值,也可以引入外部配置文件。无论是内部定义还是外部引入,都可以使用${name}获取值。
例如:我们可以将数据源配置写到外部的db.properties中,再使用properties标签引入外部配置文件,这样可以做到动态配置数据源。
1.编写db.properties
jdbc.driver = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql:///mybatis jdbc.username = root jdbc.password = root
2.在配置文件中引入db.properties
<?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 ="db.properties" > </properties > <properties resource ="db.properties" > </properties > <environments default ="mysql" > <environment id ="mysql" > <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/itbaizhan/mapper/UserMapper.xml" > </mapper > </mappers > </configuration >
当然我们也可以将数据源数据通过<properties>
配置到MyBatis配置文件内,但这样做没什么意义。
<?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 ="db.properties" > </properties > <environments default ="mysql" > <environment id ="mysql" > <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/itbaizhan/mapper/UserMapper.xml" > </mapper > </mappers > </configuration >
在MyBatis配置文件中,引入外部文件的属性为:resource
MyBatis配置文件_ <settings>
是配置MyBatis运行时的一些行为的,例如缓存、延迟加载、命名规则等一系列控制性参数。后期我们会使用该标签配置缓存和延迟加载等。
MyBatis配置文件_ <plugins>
是配置MyBatis插件的。插件可以增强MyBatis功能,比如进行sql增强,打印日志,异常处理等。后期我们会使用该标签配置分页插件。
MyBatis配置文件_
MyBatis对常用类有默认别名支持,比如java.lang.Stirng的别名为string。除此之外,我们也可以使用<typeAliases>
设置自定义别名。
为一个类配置别名 1.配置文件
<typeAliases > <typeAlias type ="全类名" alias ="别名" > </typeAlias > </typeAliases >
<typeAliases > <typeAlias type ="com.itbaizhan.pojo.User" alias ="user" > </typeAlias > <package name ="com.itbaizhan.pojo" /> </typeAliases >
2.映射文件
<select id ="findAll" resultType ="user" > select * from user </select >
为一个所有包下的所有类配置别名 <typeAliases > <package name ="包名" > </package > </typeAliases >
此时该包下的所有类都有了别名,别名省略包名,和类名相同。如:
1.配置文件:
<typeAliases > <typeAlias type ="com.itbaizhan.pojo.User" alias ="user" > </typeAlias > <package name ="com.itbaizhan.pojo" /> </typeAliases >
2.映射文件:
<select id ="findPage2" resultType ="User" parameterType ="PageQuery" > select * from user limit #{startIndex},#{pageSize} </select >
MyBatis配置文件_
<environments>
可以为MyBatis配置数据环境。
事务管理 <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="JDBC" > </transactionManager > </environment > </environments >
连接池 <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > </dataSource > </environment > </environments >
dataSource的type属性;
POOLED:使用连接池管理连接,使用MyBatis自带的连接池
UNPOOLED:不使用连接池,直接由JDBC连接
JNDI:由JAVAEE服务器管理连接,如果使用Tomcat作为服务器则使用Tomcat自带的连接池管理
MyBatis配置文件_ <mappers>
用于注册映射文件或持久层接口,只有注册的映射文件才能使用,共有四种方式都可以完成注册:
<mappers > <mapper resource ="com/itbaizhan/mapper/UserMapper.xml" > </mapper > <mapper url ="file:///E:\Eclipse_workspace1\MyBatisCase\mybatisDemo1\src\main\resources\com\itbaizhan\mapper\UserMapper.xml" > </mapper > <mapper class ="com.itbaizhan.mapper.UserMapper" > </mapper > <package name ="com.itbaizhan.mapper" /> </mappers >
MyBatis映射文件_
MyBatis映射文件中除了<insert>
、<delete>
、<update>
、<select>
外,还有一些标签可以使用:
resultMap 标签的作用的自定义映射关系。
MyBatis可以将数据库结果集封装到对象中,是因为结果集的列名和对象属性名相同:
MyBaits可以将数据库结果集封装到对象中,是因为结果集的列名和对面属性名相同;
当POJO属性名和数据库列名不一致时,MyBatis无法自动完成映射关系。如:
这个时候有两种解决方案:
1.Sql语句的查询字段起与POJO属性相同的别名。
<?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.itbaizhan.mapper.TeacherMapper" > <select id ="findAll" resultType ="com.itbaizhan.pojo.Teacher" > select tid as id, tname as teacherName from teacher--> </select > </mapper >
2.自定义映射关系、
在映射文件中,使用<resultMap>
自定义映射关系:
<resultMap id ="teacherMapper" type ="com.itbaizhan.pojo.Teacher" > <id property ="id" column ="tid" > </id > <result property ="teacherName" column ="tname" > </result > </resultMap > <resultMap id ="teacherMapper" type ="com.itbaizhan.pojo.Teacher" > <id property ="id" column ="tid" > </id > <result property ="teacherName" column ="tname" > </result > </resultMap >
在标签中,使用resultMap属性代替resultType属性,使用自定义映射关系
<?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.itbaizhan.mapper.TeacherMapper" > <resultMap id ="teacherMapper" type ="com.itbaizhan.pojo.Teacher" > <id property ="id" column ="tid" > </id > <result property ="teacherName" column ="tname" > </result > </resultMap > <select id ="findAll" resultMap ="teacherMapper" > select * from teacher </select > </mapper >
MyBatis映射文件_
用来定义可重用的Sql片段,通过 引入该片段。如:Sql语句的查询字段起与POJO属性相同的别名,该Sql片段就可以重用
//id是随便起的 <sql id ="selectAllField" > select tid as id, tname as teacherName </sql > <select id ="findAll" resultType ="com.itbaizhan.pojo.Teacher" > <include refid ="selectAllField" > </include > > from teacher </select > <select id ="findById" resultType ="com.itbaizhan.pojo.Teacher" parameterType ="int" > <include refid ="selectAllField" > </include > > from teacher where tid = #{id} </select >
MyBatis映射文件_特殊字符处理 在Mybatis映射文件中尽量不要使用一些特殊字符,如:<
,>
等。
我们可以使用符号的实体来表示:(点击实体即可查看)
符号
实体
<
<
>
>
&
&
‘
'
“
"
例子:
<select id ="findById2" resultType ="com.itbaizhan.pojo.Teacher" parameterType ="int" > <include refid ="selectAllField" > </include > from teacher where tid > #{id} </select >
@Test public void testFindById2 () { List<Teacher> byId2 = teacherMapper.findById2(2 ); byId2.forEach(System.out::println); }
动态SQL_
一个查询的方法的Sql语句不一定是固定的。比如电商网站的查询商品,用户使用不同条件查询,Sql语句就会添加不同的查询条件。此时就需要在方法中使用动态Sql语句。
<if>
标签内的Sql片段在满足条件后才会添加,用法为:<if test="条件">
。例如:根据不同条件查询用户:
1.持久层接口添加方法
package com.itbaizhan.mapper;import com.itbaizhan.pojo.User;import java.util.List;public interface UserMapper2 { List<User> findByCondition (User user) ; List<User> findByCondition (User user) ; }
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" > <mapper namespace ="com.itbaizhan.mapper.TeacherMapper" > <select id ="findByCondition" parameterType ="com.itbaizhan.pojo.User" resultType ="com.itbaizhan.pojo.User" > select * from user where 1 = 1 <if test ="username != null and username.length()!=0 " > and username like #{username} </if > <if test ="sex != null and sex.length()!=0 " > and sex like = #{sex} </if > <if test ="address != null and address.length()!=0 " > and address = #{sex} </if > </select > <select id ="findByCondition" parameterType ="com.itbaizhan.pojo.User" resultType ="com.itbaizhan.pojo.User" > select * from user where 1 = 1 <if test ="username != null and username.length()!=0 " > and username like #{username} </if > <if test ="sex != null and sex.length()!=0 " > and sex like = #{sex} </if > <if text ="address != null and address.length()!=0" > and address = #{address} </if > </select > </mapper >
3.编写测试方法
import com.itbaizhan.mapper.UserMapper;import com.itbaizhan.mapper.UserMapper2;import com.itbaizhan.pojo.PageQuery;import com.itbaizhan.pojo.User;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.util.HashMap;import java.util.List;import java.util.Map;public class TestUserMapper3 { InputStream is = null ; SqlSession session = null ; UserMapper2 userMapper2 = null ; @Before public void before () throws IOException { is = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = builder.build(is); session = factory.openSession(); userMapper2 = session.getMapper(UserMapper2.class); } @After public void after () throws IOException { session.close(); is.close(); } @Test public void testfindByCondition () { User user = new User (); List<User> users = userMapper2.findByCondition(user); user.setUsername("%尚学堂%" ); List<User> users1 = userMapper2.findByCondition(user); users1.forEach(System.out::println); user.setAddress("北京" ); List<User> users2 = userMapper2.findByCondition(user); users2.forEach(System.out::println); } }
if中的条件不能使用&&/||,而应该使用and/or
if中的条件可以直接通过属性名获取参数POJO的属性值,并且该值可以调用方法。
where后为什么要加1=1?
任意条件都可能拼接到Sql中。如果有多个条件,从第二个条件开始前都需要加And关键字。加上1=1这个永久成立的条件,就不需要考虑后面的条件哪个是第一个条件,后面的条件前都加And关键字即可。(where 1=1 是为了避免where 关键字后面的第一个词直接就是 “and”而导致语法错误。)
动态SQL_ <where>
可以代替sql中的where 1=1 和第一个and,更符合程序员的开发习惯,使用<where>
后的映射文件如下:
<select id ="findByCondition" parameterType ="com.itbaizhan.pojo.User" resultType ="com.itbaizhan.pojo.User" > select * from user <where > <if test ="username != null and username.length()!=0 " > and username like #{username} </if > <if test ="sex != null and sex.length()!=0 " > and sex like = #{sex} </if > <if test ="address != null and address.length()!=0 " > and address = #{sex} </if > </where > </select >
动态SQL_ <set>
标签用在update语句中。借助<if>
,可以只对有具体值的字段进行更新。<set>
会自动添加set关键字,并去掉最后一个if语句中多余的逗号。
<update id ="updateUser" parameterType ="com.itbaizhan.pojo.User" > update user <set > <if test ="username != null and username.length != 0" > username = #{username}, </if > <if test ="sex != null and sex.length != 0" > sex = #{sex}, </if > <if test ="address != null and address.length != 0" > address = #{address} </if > </set > <where > id = #{id} </where > </update >
动态SQL_
这些标签表示多条件分支,类似JAVA中的switch...case
。<choose>
类似switch
,<when>
类似case
,<otherwise>
类似default
,用法如下:
1.持久层方法编写
List<User> findByUsername (String name) ;
2.映射文件
<select id ="findByUsername" parameterType ="String" resultType ="com.itbaizhan.pojo.User" > select * from user <where > <choose > <when test ="username.length() < 5" > <bind name ="likename" value ="'%'+username+'%'" /> username like #{likename} </when > <when test ="username.length() < 10" > username = #{username} </when > <otherwise > id = 1 </otherwise > </choose > </where > </select >
3.测试方法
@Test public void testFindByUsername () { List<User> users = userMapper2.findByUsername("北京" ); for (User user : users) { System.out.println(user.toString()); } }
这段代码的含义为:用户名<5时使用模糊查询,用户名>=5并且<10时使用精确查询,否则查询id为1的用户
动态SQL_ <foreach>
类似JAVA中的for循环,可以遍历集合或数组。<foreach>
有如下属性:
遍历数组 我们使用<foreach>
遍历数组进行批量删除。
1.持久层接口添加方法
void deleteBatch (int [] ids) ;
2.映射文件添加标签
<delete id ="deleteBatch" parameterType ="int" > delete from user <where > <foreach collection ="array" open ="id in (" close =")" separator ="," item ="id" > #{id} </foreach > </where > </delete >
3.编写测试方法
@Test public void testDeleteBatch () { int [] ids = {6 ,7 }; userMapper2.deleteBatch(ids); session.commit(); }
遍历Collection <foreach>
遍历List和Set的方法是一样的,我们使用<foreach>
遍历List进行批量添加。
1.持久层接口添加方法
void insertBatch (List<User> users) ;
2.映射文件添加标签
<insert id ="insertBatch" parameterType ="com.itbaizhan.pojo.User" > insert into user values <foreach collection ="list" separator ="," item ="user" > (null,#{user.username},#{user.sex},#{user.address}) </foreach > </insert >
3.编写测试方法
@Test public void testInsertBatch () { User user1 = new User ("程序员1" ,"男" ,"北京" ); User user2 = new User ("程序员2" ,"女" ,"肇庆" ); List<User> users = new ArrayList <>(); users.add(user1); users.add(user2); userMapper2.insertBatch(users); session.commit(); }
遍历Map 我们使用<foreach>
遍历Map进行多条件查询。
1.持久层接口添加方法
List<User> findUser (@Param("queryMap") Map<String,Object> map) ;
2.映射文件添加标签
<select id ="findUser" parameterType ="map" resultType ="com.itbaizhan.pojo.User" > select * from user <where > <foreach collection ="queryMap" separator ="and" index ="key" item ="value" > ${key} = #{value} </foreach > </where > </select >
3.编写测试方法
@Test public void testFindUser () { Map<String,Object> queryMap = new HashMap <>(); queryMap.put("sex" ,"男" ); queryMap.put("address" ,"北京" ); List<User> users = userMapper2.findUser(queryMap); for (User user : users) { System.out.println(user.toString()); } }
缓存介绍
缓存是内存当中一块存储数据的区域,目的是提高查询 效率。MyBatis会将查询结果存储在缓存当中,当下次执行相同 的SQL时不访问数据库,而是直接从缓存中获取结果,从而减少服务器的压力。
什么是缓存?
存在于内存中的一块数据。
缓存有什么作用?
减少程序和数据库的交互,提高查询效率,降低服务器和数据库的压力。
什么样的数据使用缓存?
经常查询但不常改变的,改变后对结果影响不大的数据。
MyBatis缓存分为哪几类?
一级缓存和二级缓存
如何判断两次Sql是相同的?
查询的Sql语句相同
传递的参数值相同
对结果集的要求相同
预编译的模板Id相同
MyBatis缓存_MyBaits一级缓存
MyBatis一级缓存也叫本地缓存 。SqlSession对象中包含一个Executor对象,Executor对象中包含一个PerpetualCache对象,在该对象存放一级缓存数据。
由于一级缓存是在SqlSession对象中,所以只有使用同一个SqlSession对象操作数据库时才能共享一级缓存。
MyBatis的一级缓存是默认开启的,不需要任何的配置。
测试一级缓存 import com.itbaizhan.mapper.UserMapper;import com.itbaizhan.pojo.User;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.util.List;public class TestUserMapper4 { @Test public void testCache1 () throws IOException { InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = builder.build(is); SqlSession sesison = factory.openSession(); UserMapper mapper1 = sesison.getMapper(UserMapper.class); UserMapper mapper2 = sesison.getMapper(UserMapper.class); User user1 = mapper1.findById(1 ); System.out.println(user1.hashCode()); System.out.println("----------------------------" ); User user2 = mapper2.findById(1 ); System.out.println(user1.hashCode()); } @Test public void testCache2 () throws IOException { InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = builder.build(is); SqlSession sesison1 = factory.openSession(); SqlSession sesison2 = factory.openSession(); UserMapper mapper1 = sesison1.getMapper(UserMapper.class); UserMapper mapper2 = sesison2.getMapper(UserMapper.class); User user1 = mapper1.findById(1 ); System.out.println(user1.hashCode()); System.out.println("----------------------------" ); User user2 = mapper2.findById(1 ); System.out.println(user2.hashCode()); } }
MyBatis缓存_清除一级缓存
进行以下操作可以清空MyBatis一级缓存:
SqlSession
调用close()
:操作后SqlSession对象不可用,该对象的缓存数据也不可用。
SqlSession
调用clearCache()
/commit()
:操作会清空一级缓存数据。
SqlSession
调用增删改方法:操作会清空一级缓存数据,因为增删改后数据库发生改变,缓存数据将不准确。
@Test public void testCache3 () throws IOException { InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = builder.build(is); SqlSession session = factory.openSession(); UserMapper mapper1 = session.getMapper(UserMapper.class); UserMapper mapper2 = session.getMapper(UserMapper.class); User user1 = mapper1.findById(1 ); System.out.println(user1.hashCode()); session.clearCache(); System.out.println("----------------------------" ); User user2 = mapper2.findById(1 ); System.out.println(user2.hashCode()); }
MyBaits二级缓存
MyBatis二级缓存也叫全局缓存。数据存放在SqlSessionFactory中,只要是同一个工厂对象创建的SqlSession,在进行查询时都能共享数据。 一般在项目中只有一个SqlSessionFactory对象,所以二级缓存的数据是全项目共享的。
MyBatis一级缓存存放的是对象,二级缓存存放的是对象的数据。所以要求二级缓存存放的POJO必须是可序列化的,也就是要实现Serializable接口。
MyBatis二级缓存默认不开启,手动开启后数据先存放在一级缓存中,只有一级缓存数据清空后,数据才会存到二级缓存中。
SqlSession
调用clearCache()
无法将数据存到二级缓存中
一级缓存存放的是对象,二级缓存存放的是对象的数据 开启二级缓存 1.POJO类实现Serializable接口。
public class User implements Serializable { private int id; private String username; private String sex; private String address; }
2.在MyBatis核心配置文件添加如下设置:
<settings > <setting name ="cacheEnabled" value ="true" /> </settings >
由于cacheEnabled默认值是true,所以该设置可以省略。
3.在映射文件添加<cache />
标签,该映射文件下的所有方法都支持二级缓存。
如果查询到的集合中对象过多,二级缓存只能缓存1024个对象引用。可以通过<cache />
标签的size属性修改该数量。
4.测试二级缓存
@Test public void testCache4 () throws IOException { InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = builder.build(is); SqlSession sesison1 = factory.openSession(); SqlSession sesison2 = factory.openSession(); UserMapper mapper1 = sesison1.getMapper(UserMapper.class); UserMapper mapper2 = sesison2.getMapper(UserMapper.class); User user1 = mapper1.findById(1 ); System.out.println(user1); System.out.println(user1.hashCode()); sesison1.commit(); System.out.println("----------------------------" ); User user2 = mapper2.findById(1 ); System.out.println(user2); System.out.println(user2.hashCode()); }
MyBatis关联查询 MyBatis的关联查询分为一对一关联查询和一对多关联查询
查询对象时,将关联的另一个对象查询出来,就是一对一关联查询。
查询对象时,将关联的另一个对象的集合查询出来,就是一对多关联查询
例如有学生类和班级类:
一个学生对应一个班级,也就是学生类中有一个班级属性,这就是一对一关系。
一个班级对应多个学生,也就是班级类中有一个学生集合属性,这就是一对多关系。
实体类设计如下:
package com.itbaizhan.pojo;public class Student { private int sid; private String name; private int age; private String sex; private Classes classes; public int getSid () { return sid; } public void setSid (int sid) { this .sid = sid; } public String getName () { return name; } @Override public String toString () { return "Student{" + "sid=" + sid + ", name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + ", classes=" + classes + '}' ; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } public String getSex () { return sex; } public void setSex (String sex) { this .sex = sex; } public Classes getClasses () { return classes; } public void setClasses (Classes classes) { this .classes = classes; } }
package com.itbaizhan.pojo;import java.util.List;public class Classes { private int id; private String className; private List<Student> studentList; public int getId () { return id; } public void setId (int id) { this .id = id; } public String getClassName () { return className; } public void setClassName (String className) { this .className = className; } public List<Student> getStudentList () { return studentList; } public void setStudentList (List<Student> studentList) { this .studentList = studentList; } }
MyBatis一对一关联查询 查询学生时,将关联的一个班级对象查询出来,就是一对一关联查询
创建持久层接口 public interface StudentMapper { List<Student> findAll () ; }
创建映射文件 <?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.itbaizhan.mapper.StudentMapper" > <resultMap id ="studentMapper" type ="com.itbaizhan.pojo.Student" > <id property ="sid" column ="sid" > </id > <result property ="name" column ="name" > </result > <result property ="age" column ="age" > </result > <result property ="sex" column ="sex" > </result > <association property ="classes" column ="classId" javaType ="com.itbaizhan.pojo.Classes" > <id property ="cid" column ="cid" > </id > <result property ="className" column ="className" > </result > </association > <association property ="classes" column ="classId" javaType ="com.itbaizhan.pojo.Classes" > <id property = "cid" column ="cid" > </id > <result property ="className" column ="className" > </result > </association > </resultMap > <select id ="findAll" resultMap ="studentMapper" > select * from student left join classes on student.classId = classes.cid; select * from student left join classes on student.classId = classes.cid; </select > </mapper >
配置文件注册映射文件 <mappers > <package name ="com.itbaizhan.mapper" /> </mappers >
测试一对一关联查询 import com.itbaizhan.mapper.StudentMapper;import com.itbaizhan.pojo.Student;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.util.List;public class TestManyTableQuery { InputStream is = null ; SqlSession session = null ; @Before public void before () throws IOException { is = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = builder.build(is); session = factory.openSession(); } @After public void after () throws IOException { session.close(); is.close(); } @Test public void testFindAllStudent () { StudentMapper studentMapper = session.getMapper(StudentMapper.class); List<Student> all = studentMapper.findAll(); all.forEach(System.out::println); } }
MyBatis一对多查询 查询班级时,将关联的学生集合查询出来,就是一对多关联查询。
创建持久层接口 public interface ClassesMapper { List<Classes> findAll () ; }
创建映射文件 <?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.itbaizhan.mapper.ClassesMapper" > <resultMap id ="classesMapper" type ="com.itbaizhan.pojo.Classes" > <id property ="cid" column ="cid" > </id > <result property ="className" column ="className" > </result > <collection property ="studentList" column ="classId" ofType ="com.itbaizhan.pojo.Student" > <id property ="sid" column ="sid" > </id > <result property ="name" column ="name" > </result > <result property ="age" column ="age" > </result > <result property ="sex" column ="sex" > </result > </collection > <collection property ="studentList" column ="classId" ofType ="com.itbaizhan.pojo.Student" > <id property ="sid" column ="sid" > </id > <result property ="name" column ="name" > </result > <result property ="age" column ="age" > </result > <result property ="sex" column ="sex" > </result > </collection > </resultMap > <select id ="findAll" resultMap ="classesMapper" > select * from classes left join student on classes.cid = student.classId; select * from classes left join student on classes.cid = student.classId; </select > </mapper >
测试一对多关联查询 @Test public void testFindAllClasses () { ClassesMapper classesMapper = session.getMapper(ClassesMapper.class); List<Classes> all = classesMapper.findAll(); for (Classes classes : all) { System.out.println(classes.toString()); } }
Mybatis映射文件表示关联集合列的标签是
MyBatis多对多关联查询 MyBatis多对多关联查询本质就是两个一对多关联查询。
例如有老师类和班级类:
一个老师对应多个班级,也就是老师类中有一个班级集合属性。
一个班级对应多个老师,也就是班级类中有一个老师集合属性。
实体类设计如下:
package com.itbaizhan.pojo;import java.util.List;public class Classes { private Integer cid; private String className; private List<Student> studentList; private List<Teacher> teacherList; public Integer getCid () { return cid; } public void setCid (Integer cid) { this .cid = cid; } public String getClassName () { return className; } public void setClassName (String className) { this .className = className; } public List<Student> getStudentList () { return studentList; } public void setStudentList (List<Student> studentList) { this .studentList = studentList; } public List<Teacher> getTeacherList () { return teacherList; } public void setTeacherList (List<Teacher> teacherList) { this .teacherList = teacherList; } @Override public String toString () { return "Classes{" + "cid=" + cid + ", className='" + className + '\'' + ", studentList=" + studentList + ", teacherList=" + teacherList + '}' ; } }
package com.itbaizhan.pojo;import java.util.List;public class Teacher { private Integer tid; private String tname; private List<Classes> classes; public Integer getTid () { return tid; } public void setTid (Integer tid) { this .tid = tid; } public String getTname () { return tname; } public void setTname (String tname) { this .tname = tname; } public List<Classes> getClasses () { return classes; } public void setClasses (List<Classes> classes) { this .classes = classes; } @Override public String toString () { return "Teacher{" + "tid=" + tid + ", tname='" + tname + '\'' + ", classes=" + classes + '}' ; } }
在数据库设计中,需要建立中间表,双方与中间表均为一对多关系。
接下来测试查询老师时,将关联的班级集合查询出来
创建持久层接口 public interface TeacherMapper {List<Teacher> findAll () ; }
创建映射文件 <?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.itbaizhan.mapper.TeacherMapper" > <resultMap id ="teacherMapper" type ="com.itbaizhan.pojo.Teacher" > <id property ="tid" column ="tid" > </id > <result property ="tname" column ="tname" > </result > <collection property ="classes" column ="tid" ofType ="com.itbaizhan.pojo.Classes" > <id column ="cid" property ="cid" > </id > <result property ="className" column ="className" > </result > </collection > </resultMap > <select id ="findAll" resultMap ="teacherMapper" > select * from teacher left join classes_teacher on teacher.tid = classes_teacher.tid left join classes on classes_teacher.cid = classes.cid </select > </mapper >
测试多对多关联查询 @Test public void testFindAllTeacher () { TeacherMapper teacherMapper = session.getMapper(TeacherMapper.class); List<Teacher> all = teacherMapper.findAll(); all.forEach(System.out::println); }
如果想查询班级时,将关联的老师集合查询出来,只需要修改班级映射文件的Sql语句和<resultMap>
即可:
<?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.itbaizhan.mapper.ClassesMapper" > <resultMap id ="classesMapper" type ="com.itbaizhan.pojo.Classes" > <id property ="cid" column ="cid" > </id > <result property ="className" column ="className" > </result > <collection property ="studentList" column ="classId" ofType ="com.itbaizhan.pojo.Student" > <id property ="sid" column ="sid" > </id > <result property ="name" column ="name" > </result > <result property ="age" column ="age" > </result > <result property ="sex" column ="sex" > </result > </collection > <collection property ="teacherList" column ="cid" ofType ="com.itbaizhan.pojo.Teacher" > <id property ="tid" column ="tid" > </id > <result property ="tname" column ="tname" > </result > </collection > </resultMap > <select id ="findAll" resultMap ="classesMapper" > select * from classes left join student on classes.cid = student.classId left join classes_teacher on classes.cid = classes_teacher.cid left join teacher on classes_teacher.tid = teacher.tid </select > </mapper >
关于MyBatis多对多关系,以下说法正确的是:
两方实体类中都有另一方实体类的集合属性
MyBatis分解式查询_一对多 在MyBatis多表查询中,使用连接查询时一个Sql语句就可以查询出所有的数据。如:
# 查询班级时关联查询出学生 select * from classes left join student on student.classId = classes.cid
也可以使用分解式查询,即将一个连接Sql语句分解为多条Sql语句,如:
# 查询班级时关联查询出学生 select * from classes;select * from student where classId = 1 ;select * from student where classId = 2 ;
这种写法也叫N+1查询。
连接查询:
优点:降低查询次数,从而提高查询效率。
缺点:如果查询返回的结果集较多会消耗内存空间。
N+1查询:
优点:结果集分步获取,节省内存空间。
缺点:由于需要执行多次查询,相比连接查询效率低。
我们以查询班级时关联查询出学生为例,使用N+1查询:
创建每个查询语句的持久层方法 public interface ClassesMapper { List<Classes> findAll () ; } public interface StudentMapper { List<Student> findByClassId (int classId) ; }
在映射文件中进行配置 <?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.itbaizhan.mapper2.StudentMapper2" > <select id ="findByClassId" resultType ="com.itbaizhan.pojo.Student" parameterType ="int" > select * from student where classId = ${classId} </select > </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" > <mapper namespace ="com.itbaizhan.mapper2.ClassesMapper2" > <resultMap id ="MyClassesMapper" type ="com.itbaizhan.pojo.Classes" > <id property ="cid" column ="cid" > </id > <result property ="className" column ="className" > </result > <collection property ="studentList" ofType ="com.itbaizhan.pojo.Student" select ="com.itbaizhan.mapper2.StudentMapper2.findByClassId" column ="cid" > </collection > </resultMap > <select id ="findAll" resultMap ="MyClassesMapper" > select * from classes </select > </mapper >
测试查询方法
@Test public void testFindAllClasses2 () { ClassesMapper2 classesMapper2 = session.getMapper(ClassesMapper2.class); List<Classes> all = classesMapper2.findAll(); all.forEach(System.out::println); }
MyBatis分解式查询_一对一 查询学生时关联查询出班级也可以使用分解式查询,首先将查询语句分开:
select * from student;select * from classes where cid = ?;
创建每个查询语句的持久层方法 public interface StudentMapper { List<Student> findAll () ; } public interface ClassesMapper { Classes findByCid (int cid) ; }
在映射文件中进行配置 <?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.itbaizhan.mapper2.ClassesMapper2" > <resultMap id ="MyClassesMapper" type ="com.itbaizhan.pojo.Classes" > <id property ="cid" column ="cid" > </id > <result property ="className" column ="className" > </result > <collection property ="studentList" ofType ="com.itbaizhan.pojo.Student" select ="com.itbaizhan.mapper2.StudentMapper2.findByClassId" column ="cid" > </collection > </resultMap > <select id ="findAll" resultMap ="MyClassesMapper" > select * from classes </select > <select id ="findByCid" resultType ="com.itbaizhan.pojo.Classes" parameterType ="int" > select * from classes where cid = ${cid} </select > </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" > <mapper namespace ="com.itbaizhan.mapper2.StudentMapper2" > <select id ="findByClassId" resultType ="com.itbaizhan.pojo.Student" parameterType ="int" > select * from student where classId = ${classId} </select > <resultMap id ="MyStudentMapper" type ="com.itbaizhan.pojo.Student" > <id property ="sid" column ="sid" > </id > <result property ="name" column ="name" > </result > <result property ="age" column ="age" > </result > <result property ="sex" column ="sex" > </result > <association property ="classes" javaType ="com.itbaizhan.pojo.Classes" select ="com.itbaizhan.mapper2.ClassesMapper2.findByCid" column ="classId" > </association > </resultMap > <select id ="findAll" resultMap ="MyStudentMapper" > select * from student </select > </mapper >
测试查询方法 @Test public void testFindAllStudent2 () { StudentMapper2 studentMapper2 = session.getMapper(StudentMapper2.class); List<Student> all = studentMapper2.findAll(); all.forEach(System.out::println); }
MyBatis延迟加载 分解式查询又分为两种加载方式:
立即加载:在查询主表时就执行所有的Sql语句。
延迟加载:又叫懒加载,首先执行主表的查询语句,使用从表数据时才触发从表的查询语句。
延迟加载在获取关联数据时速度较慢,但可以节约资源,即用即取。
开启延迟加载 设置所有的N+1查询都为延迟加载:
<settings > <setting name ="lazyLoadingEnabled" value ="true" /> </settings >
<collection property ="studentList" ofType ="com.itbaizhan.pojo.Student" select ="com.itbaizhan.mapper2.StudentMapper2.findByClassId" column ="cid" fetchType ="lazy" ></collection >
测试延迟加载 @Test public void testFindAllClasses2 () { ClassesMapper2 classesMapper2 = session.getMapper(ClassesMapper2.class); List<Classes> all = classesMapper2.findAll(); all.forEach(System.out::println); System.out.println("-------------------------" ); System.out.println(all.get(0 ).getStudentList()); }
由于打印对象时会调用对象的toString
方法,toString
方法默认会触发延迟加载的查询,所以我们无法测试出延迟加载的效果。
我们在配置文件设置lazyLoadTriggerMethods属性,该属性指定对象的什么方法触发延迟加载,设置为空字符串即可。
<settings > <setting name ="lazyLoadTriggerMethods" value ="" /> </settings >
一般情况下,一对多查询使用延迟加载,一对一查询使用立即加载。
MyBatis注解开发_环境搭建 MyBatis可以使用注解替代映射文件。映射文件的作用就是定义Sql语句,可以在持久层接口上使用@Select/@Delete/@Insert/@Update定义Sql语句,这样就不需要使用映射文件了。
创建maven工程,引入依赖
创建mybatis核心配置文件SqlMapConfig.xml
将log4j.properties文件放入resources中,让控制台打印SQL语句。
创建实体类
创建持久层接口,并在接口方法上定义Sql语句
package com.itbaizhan.mapper;import com.itbaizhan.pojo.User;import org.apache.ibatis.annotations.Select;import java.util.List;public interface UserMapper { @Select("select * from user") List<User> findAll () ; }
由于注解在方法上方,而方法中就有参数类型和返回值类型,所以使用注解开发不需要定义参数类型和返回值类型
6.在核心配置文件注册持久层接口,由于没有映射文件,所以只能采用注册接口或注册包的方法。
<mappers > <package name ="com.itbaizhan.mapper" /> </mappers >
7.测试方法
import com.itbaizhan.mapper.UserMapper;import com.itbaizhan.pojo.User;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.util.List;public class TestUserMapper { InputStream resourceAsStream = null ; SqlSession session = null ; UserMapper userMapper = null ; @Before public void Before () throws IOException { resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = sqlSessionFactoryBuilder.build(resourceAsStream); session = factory.openSession(); userMapper = session.getMapper(UserMapper.class); } @After public void After () throws IOException { session.close(); resourceAsStream.close(); } @Test public void testfindAll () { List<User> all = userMapper.findAll(); all.forEach(System.out::println); } }
MyBatis注解开发_增删改查 接下来写一套基于MyBatis注解的增删改查方法:
@SelectKey(keyColumn = "id",keyProperty = "id",resultType = int.class,before = false,statement = "SELECT LAST_INSERT_ID()") @SelectKey(keyColumn = "id",keyProperty = "id",reslutType = int.class,before = false,statement = "SELECT LAST_INSERT_ID()") @Insert("insert into user(username,sex,address) values (#{username},#{sex},#{address})") void add (User user) ;@Update("update user set username = #{username},sex=#{sex},address=#{address} where id = #{id}") void update (User user) ;@Delete("delete from user where id = #{id}") void delete (int id) ;@Select("select * from user where username like #{username}") @Select("select * from user where username like #{username}") List<User> findByUsernameLike (String username) ;
编写测试方法:
@Test public void testallUser () { User user = new User ("失效" , "男" , "火星" ); System.out.println(user); userMapper.add(user); session.commit(); System.out.println(user); } @Test public void testUpdate () { User user = new User (11 , "天选之人" , "男" , "肇庆" ); userMapper.update(user); session.commit(); System.out.println(user); } @Test public void testDelete () { userMapper.delete(11 ); session.commit(); List<User> all = userMapper.findAll(); all.forEach(System.out::println); } @Test public void testfindByUsernameLike () { List<User> lists = userMapper.findByUsernameLike("程序员" ); lists.forEach(System.out::println); }
MyBatis注解开发_动态Sql
MyBatis注解开发中有两种方式构建动态Sql:
使用脚本标签 将Sql嵌套在<script>
内即可使用动态Sql标签:
@Select("<script>" + " select * from user\n" + " <where>\n" + " <if test=\"username != null and username.length() != 0\">\n" + " username like #{username}\n" + " </if>\n" + " <if test=\"sex != null and sex.length() != 0\">\n" + " and sex = #{sex}\n" + " </if>\n" + " <if test=\"address != null and address.length() != 0\">\n" + " and address = #{address}\n" + " </if>\n" + " </where>" + "</script>") List<User> findByCondition (User user) ;
在方法中构建动态Sql 在MyBatis中有@SelectProvider
、@UpdateProvider
、@DeleteProvider
、@InsertProvider
注解。当使用这些注解时将不在注解中直接编写SQL,而是调用某个类的方法来生成SQL。
@SelectProvider(type = UserProvider.class,method = "findByConditionSql") List<User> findByCondition (User user) ;
package com.itbaizhan.provider;import com.itbaizhan.pojo.User;public class UserProvider { public String findByConditionSql (User user) { StringBuffer sb = new StringBuffer ("select * from user where 1=1 " ); if (user.getUsername()!=null &&user.getUsername().length()!=0 ) { sb.append("and username like #{username}" ); } if (user.getSex()!=null &&user.getSex().length()!=0 ) { sb.append("and sex = #{sex}" ); } if (user.getAddress()!=null &&user.getAddress().length()!=0 ) { sb.append("and address = #{address}" ); } return sb.toString(); } }
@Test public void findByCondition () { User user = new User (); user.setUsername("%北京%" ); List<User> all = userMapper.findByCondition(user); all.forEach(System.out::println); }
MyBatis注解开发_自定义映射关系 当POJO属性名与数据库列名不一致时,需要自定义实体类和结果集的映射关系,在MyBatis注解开发中,使用@Results定义并使用自定义映射,使用@ResultMap使用自定义映射,用法如下:
@Results(id = "userDiyMapper",value = { @Result(id = true,property = "id",column = "id"), @Result(property = "username",column = "username"), @Result(property = "sex",column = "sex"), @Result(property = "address",column = "address"), }) @Select("select * from user") List<User> findAll () ; @ResultMap("userDiyMapper") @Select("select * from user where id = #{id}") User findById (int id) ;
@Test public void findById () { User byId = userMapper.findById(2 ); System.out.println(byId); }
MyBatis注解开发_二级缓存
MyBatis默认开启一级缓存,接下来我们学习如何在注解开发时使用二级缓存:
1.POJO类实现Serializable接口。
2.在MyBatis配置文件添加如下设置:
<settings > <setting name ="cacheEnabled" value ="true" /> </settings >
3.在持久层接口上方加注解@CacheNamespace(blocking=true),该接口的所有方法都支持二级缓存
4.测试二级缓存
import com.itbaizhan.mapper.UserMapper;import com.itbaizhan.pojo.User;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.util.List;public class TestUserMapper2 { @Test public void testCache () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = sqlSessionFactoryBuilder.build(resourceAsStream); SqlSession session1 = factory.openSession(); SqlSession session2 = factory.openSession(); User user1 = session1.getMapper(UserMapper.class).findById(1 ); System.out.println(user1); System.out.println(user1.hashCode()); session1.commit(); User user2 = session2.getMapper(UserMapper.class).findById(2 ); System.out.println(user2); System.out.println(user2.hashCode()); } }
MyBatis注解开发_一对一关联查询
在MyBatis的注解开发中对于多表查询只支持分解查询,不支持连接查询
1.创建实体类
public class Student { private int sid; private String name; private int age; private String sex; private Classes classes; } public class Classes { private int cid; private String className; private List<Student> students; }
2.创建分解后的查询方法
public interface StudentMapper { @Select("select * from student") List<Student> findAll () ; } public interface ClassesMapper { @Select("select * from classes where cid = #{cid}") Classes findByCid (Integer cid) ; }
3.主表的查询配置自定义映射关系
package com.itbaizhan.mapper;import com.itbaizhan.pojo.Student;import org.apache.ibatis.annotations.One;import org.apache.ibatis.annotations.Result;import org.apache.ibatis.annotations.Results;import org.apache.ibatis.annotations.Select;import org.apache.ibatis.mapping.FetchType;import java.util.List;public interface StudentMapper { @Results(id = "studentMapper",value = { @Result(id = true,property = "sid",column = "sid"), @Result(property = "name",column = "name"), @Result(property = "age",column = "age"), @Result(property = "sex",column = "sex"), /** * property:属性名 * column:调用从表方法时传入的参数列 * one:表示该属性是一个对象 * select:调用的从表方法 * fetchType:加载方法 * */ @Result(property = "classes",column = "classId", one = @One(select = "com.itbaizhan.mapper.ClassesMapper.findByCid", fetchType = FetchType.EAGER )) }) @Select("select * from student") List<Student> findAll () ; }
4.测试
import com.itbaizhan.mapper.StudentMapper;import com.itbaizhan.mapper.UserMapper;import com.itbaizhan.pojo.Student;import com.itbaizhan.pojo.User;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.util.List;public class TestManyTable { InputStream resourceAsStream = null ; SqlSession session = null ; @Before public void Before () throws IOException { resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = sqlSessionFactoryBuilder.build(resourceAsStream); session = factory.openSession(); } @After public void After () throws IOException { session.close(); resourceAsStream.close(); } @Test public void findAllStudent () { StudentMapper mapper = session.getMapper(StudentMapper.class); List<Student> all = mapper.findAll(); for (Student student : all) { System.out.println(student.toString()); } } }
1.在MyBatis中,@Reuslt内定义属性表示该字段为对象类型
one
2.在MyBatis中使用注解实现多表查询,以下说法正确的是:
只支持分解查询。
MyBatis注解开发_一对多关联查询 1.创建分解后的查询方法
public interface ClassesMapper { @Select("select * from classes") List<Classes> findAll () ; } public interface StudentMapper { @Select("select * from student where classId = #{classId}") List<Student> findByClassId (int classId) ; }
2.主表的查询配置自定义映射关系
@Results(id = "classMapper",value = { @Result(id = true,property = "cid",column = "cid"), @Result(property = "className", column = "className"), //many:表示该属性是一个集合 @Result(property = "studentList", column = "cid", many = @Many(select = "com.itbaizhan.mapper.StudentMapper.findByClassId", fetchType = FetchType.LAZY)) }) @Select("select * from classes") List<Classes> findAll () ;
3.测试
@Test public void findAllClasses () { ClassesMapper classesMapper = session.getMapper(ClassesMapper.class); List<Classes> all = classesMapper.findAll(); all.forEach(System.out::println); }
注解开发与映射文件开发的对比
MyBatis中更推荐使用映射文件开发,Spring、SpringBoot更推荐注解方式。具体使用要视项目情况而定。它们的优点对比如下:
映射文件:
代码与Sql语句是解耦的,修改时只需修改配置文件,无需修改源码。
Sql语句集中,利于快速了解和维护项目。
级联查询支持连接查询和分解查询两种方式,注解开发只支持分解查询。
注解:
配置简单,开发效率高。
类型安全,在编译期即可进行校验,不用等到运行时才发现错误。
PageHelper分页插件
开发过程中如果要进行分页查询,需要传入页数和每页条数。返回页面数据,总条数,总页数,当前页面,每页条数等数据。此时使用PageHelper插件可以快速帮助我们获取这些数据。
PageHelper是一款非常好用的开源免费的Mybatis第三方分页插件。使用该插件时,只要传入分页参数,即可自动生成页面对象。我们使用该插件分页查询所有用户:
1.引入依赖
<dependency > <groupId > com.github.pagehelper</groupId > <artifactId > pagehelper</artifactId > <version > 5.3.0</version > </dependency >
2.Mybatis配置文件中配置PageHelper插件
<plugins > <plugin interceptor ="com.github.pagehelper.PageInterceptor" > <property name ="helperDialect" value ="mysql" /> </plugin > </plugins >
3.使用PageHelper插件
@Test public void testFindPage () { PageHelper.startPage(1 , 3 ); List<User> all = userMapper.findAll(); PageInfo pageInfo = new PageInfo (all); System.out.println("结果集:" +pageInfo.getList()); System.out.println("总条数:" +pageInfo.getTotal()); System.out.println("总页数" +pageInfo.getPages()); System.out.println("当前页" +pageInfo.getPageNum()); System.out.println("每页条数" +pageInfo.getSize()); }
MyBatis_Generator_工具引入
MyBatis Generator(MBG)是MyBatis官方提供的代码生成器。它可以根据数据库的表结构自动生成POJO类、持久层接口与映射文件,极大减少了代码的编写量,提高开发效率。
(自动生成POJO类、持久层接口与映射文件)
MBG可以作为项目引入使用,也可以作为Maven插件使用,其中作为Maven插件使用更加方便快捷。
1.准备数据库表
2.在pom文件中配置MBG插件
<?xml version="1.0" encoding="UTF-8" ?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.itbaizhan</groupId > <artifactId > mybatisDemo4</artifactId > <version > 1.0-SNAPSHOT</version > <build > <plugins > <plugin > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-maven-plugin</artifactId > <version > 1.3.7</version > <configuration > <configurationFile > src/main/resources/generatorConfig.xml</configurationFile > <verbose > true</verbose > <overwrite > true</overwrite > </configuration > </plugin > </plugins > </build > </project >
3.编写MBG配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" > <generatorConfiguration > <classPathEntry location ="E:\software\m2\repository\mysql\mysql-connector-java\8.0.26\mysql-connector-java-8.0.26.jar" /> <context id ="default" targetRuntime ="MyBatis3" > <commentGenerator > <property name ="suppressAllComments" value ="true" /> </commentGenerator > <jdbcConnection driverClass ="com.mysql.jdbc.Driver" connectionURL ="jdbc:mysql://localhost:3306/mybatis" userId ="root" password ="root" > </jdbcConnection > <javaTypeResolver > <property name ="forceBigDecimals" value ="false" /> </javaTypeResolver > <javaModelGenerator targetProject ="src/main/java" targetPackage ="com.itbaizhan.pojo" > <property name ="enableSubPackages" value ="false" /> <property name ="trimStrings" value ="true" /> </javaModelGenerator > <sqlMapGenerator targetProject ="src/main/resources" targetPackage ="com.itbaizhan.mapper" > <property name ="enableSubPackages" value ="false" /> </sqlMapGenerator > <javaClientGenerator targetProject ="src/main/java" targetPackage ="com.itbaizhan.mapper" type ="XMLMAPPER" > <property name ="enableSubPackages" value ="false" /> </javaClientGenerator > <table tableName ="product" > </table > </context > </generatorConfiguration >
Product.java:POJO类
ProductMapper.java:持久层接口
ProductMapper.xml:映射文件
ProductExample.java:查询扩展类,该类可以构造复杂的查询条件。
Criterion:代表一个字段。
GeneratedCriteria:抽象类,生成查询条件的工具。
Criteria:GeneratedCriteria的子类,生成查询条件的工具。
5.在配置文件中注册生成的映射文件
<mappers > <mapper class ="com.itbaizhan.mapper.ProductMapper" > </mapper > </mappers >
MyBatis Generator_增删改方法
import com.itbaizhan.mapper.ProductMapper;import com.itbaizhan.pojo.Product;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;public class TestMBG { InputStream resourceAsStream = null ; SqlSession session = null ; ProductMapper productMapper = null ; @Before public void Before () throws IOException { resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder (); SqlSessionFactory factory = sqlSessionFactoryBuilder.build(resourceAsStream); session = factory.openSession(); productMapper = session.getMapper(ProductMapper.class); } @After public void After () throws IOException { session.close(); resourceAsStream.close(); } @Test public void testAdd () { Product product = new Product ("百战Python课1234" ,15000.0 ); productMapper.insert(product); session.commit(); } @Test public void testUpdate () { Product product = new Product (2 ,"百战Python课" , 25000.0 ); productMapper.updateByPrimaryKey(product); session.commit(); } @Test public void testDelete () { productMapper.deleteByPrimaryKey(5 ); session.commit(); } }
Mybaits Generator生成的方法中,根据ID删除是什么方法:
deleteByPrimaryKey
MyBatis Generator_查询方法 @Test public void testFindById () { Product product = productMapper.selectByPrimaryKey(1 ); System.out.println(product); } @Test public void testFindAll () { ProductExample productExample = new ProductExample (); List<Product> products = productMapper.selectByExample(productExample); products.forEach(System.out::println); } @Test public void testFindByName () { ProductExample productExample = new ProductExample (); ProductExample.Criteria criteria = productExample.createCriteria(); criteria.andProductnameLike("%尚学堂%" ); List<Product> products = productMapper.selectByExample(productExample); products.forEach(System.out::println); }
Mybaits Generator生成的方法中,查询所有使用什么方法:
selectByExample
MyBatis Generator_复杂查询 @Test public void testFindAnd () { ProductExample productExample = new ProductExample (); ProductExample.Criteria criteria = productExample.createCriteria(); criteria.andProductnameLike("%百战%" ); criteria.andPriceBetween(0.0 ,20000.0 ); List<Product> products = productMapper.selectByExample(productExample); products.forEach(System.out::println); } @Test public void testFindOr () { ProductExample productExample = new ProductExample (); ProductExample.Criteria criteria = productExample.createCriteria(); criteria.andProductnameLike("%百战%" ); ProductExample.Criteria criteria1 = productExample.createCriteria(); criteria1.andPriceBetween(0.0 ,10000.0 ); productExample.or(criteria1); List<Product> products = productMapper.selectByExample(productExample); products.forEach(System.out::println); }
1.在Mybaits Generator中,Criteria类是类的内部类:Example
动态代理_代理模式简介
代理模式是23种设计模式之一。设计模式是前人总结的,在软件开发过程遇到常用问题的解决方案,常见的设计模式有单例模式、工厂模式、适配器模式等等。
代理模式的作用是在不修改原对象的基础上增强该对象的方法 。比如官方购买苹果手机不赠送充电头,此时京东平台作为苹果的代理商,可以在代理销售苹果手机时赠送充电头。
代理模式分为静态代理、动态代理。静态代理会生成一个代理类,动态代理不会生成代理类,直接生成代理对象
代理模式的作用是:在不修改原对象的基础上增强该对象的方法
动态代理_JDK动态代理
JDK动态代理是针对接口进行代理 ,所以我们要写被代理的接口和该接口的实现类。
package com.itbaizhan.jdkdynamic;public interface Apple { String sell (double price) ; void repair () ; }
package com.itbaizhan.jdkdynamic;public class AppleImpl implements Apple { @Override public String sell (double price) { System.out.println("产品卖了" +price+"元" ); return "HuaWei" ; } @Override public void repair () { System.out.println("苹果售后维修" ); } }
package com.itbaizhan.jdkdynamic;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class ShoppingProxy implements InvocationHandler { private Apple apple; public ShoppingProxy (Apple apple) { this .apple = apple; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName(); if ("sell" .equals(name)) { double price = (double ) args[0 ] * 0.9 ; Object result = method.invoke(apple, price); return result + "和充电头" ; } else if ("repair" .equals(name)) { System.out.println("专属客服为您服务!" ); } else { return method.invoke(apple, args); } return null ; } }
import com.itbaizhan.jdkdynamic.Apple;import com.itbaizhan.jdkdynamic.AppleImpl;import com.itbaizhan.jdkdynamic.ShoppingProxy;import java.lang.reflect.Proxy;public class Test { public static void main (String[] args) { Apple apple = new AppleImpl (); ShoppingProxy shoppingProxy = new ShoppingProxy (apple); Apple appleJD = (Apple) Proxy.newProxyInstance( apple.getClass().getClassLoader(), apple.getClass().getInterfaces(), shoppingProxy ); String sell = appleJD.sell(6000 ); System.out.println(sell); appleJD.repair(); } }
动态代理_CGLib动态代理
CGLib动态代理简化了JDK动态代理的写法,JDK是针对接口代理,而CGLib是针对类代理。
1.引入依赖
<dependencies > <dependency > <groupId > cglib</groupId > <artifactId > cglib</artifactId > <version > 3.3.0</version > </dependency > </dependencies >
package com.itbaizhan.cglibdynamic;public class Apple { public String sell (double price) { System.out.println("产品卖了" +price+"元" ); return "HuaWei" ; } public void repair () { System.out.println("苹果售后维修" ); } }
package com.itbaizhan.cglibdynamic;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class ShoppingProxy implements MethodInterceptor { private Apple apple; public ShoppingProxy (Apple apple) { this .apple = apple; } @Override public Object intercept (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { String name = method.getName(); if ("sell" .equals(name)){ double price = (double )objects[0 ]*0.8 ; Object result = method.invoke(apple, price); return result+"和数据线" ; }else if ("repair" .equals(name)){ System.out.println("专属客服为您服务!" ); return method.invoke(apple,objects); }else { return method.invoke(apple,objects); } } }
import com.itbaizhan.cglibdynamic.Apple;import com.itbaizhan.cglibdynamic.ShoppingProxy;import net.sf.cglib.proxy.Enhancer;public class Test1 { public static void main (String[] args) { Apple apple = new Apple (); ShoppingProxy shoppingProxy = new ShoppingProxy (apple); Apple appleTB = (Apple) Enhancer.create(Apple.class, shoppingProxy); String sell = appleTB.sell(9000 ); System.out.println(sell); appleTB.repair(); } }