Mybatis
1、简介
1.1、什么是Mybatis
- MyBatis 是一款优秀的持久层框架
- 它支持自定义 SQL、存储过程以及高级映射
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录
- MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了[google code](https://baike.baidu.com/item/google code/2346604),并且改名为MyBatis 。2013年11月迁移到Github。
1.2、如何获取Mybatis
-
maven:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency>
-
中文文档:https://mybatis.org/mybatis-3/zh/index.html
-
Github:https://github.com/mybatis/mybatis-3
1.3、为什么需要Mybatis
- 帮助程序员操作数据库中的数据
- 方便
- 简化传统的JDBC代码,自动化
- 更容易上手
- 使用的人多
优点
- 简单易学
- 灵活
- sql和代码的分离,提高了可维护性
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql
2、第一个Mybatis程序
思路:搭建环境——导入Mybatis——编写代码——测试
2.1、搭建环境
- 搭建数据库
CREATE DATABASE `mybatis`
USE `mybatis`;
CREATE TABLE `user`(
`id` INT NUO NULL,
`name` VARCHAR(20) NOT NULL,
`pwd` VARCHAR(20) NOT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=UTF8;
INSERT INTO `user` (id,name,pwd) VALUES('1','张三','123465'),('2','李四','123465'),('3','王五','123465')
-
创建一个普通的Maven项目
-
删除src目录(将此项目当作父工程)
-
导入maven依赖
<dependencies> <!--导入Myabtis依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <!--导入Mysql依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> </dependency> <!--导入Junit依赖--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
2.2、运行
-
创建一个Mybatis工程模块
-
编写mybatis核心配置文件mybaitis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?urlSSL=true&useEncoding=true&characterEncoding=utf-8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> </configuration>
-
编写静态数据类构建 SqlSessionFactory
public class MybatisUtils { static SqlSessionFactory sqlSessionFactory = null; static { String resource = "mybatis-config.xml"; InputStream inputStream = null; try { inputStream = Resources.getResourceAsStream(resource); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
-
编写实体类
package com.teng.entity; public class User { private int id; private String name; private String pwd; public User() { } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
-
编写接口和接口实现类(在mybatis中接口实现类转变为Mapper配置文件)
UserMapper.java
public interface UserMapper { public List<User> getUserList(); }
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"> <!-- namespace:绑定一个指定的Dao/Mapper接口 id:接口中对应的方法名称 resultType:返回结果的类型,如果是类则需要全路径 --> <mapper namespace="com.teng.Dao.UserMapper"> <select id="getUserList" resultType="com.teng.entity.User"> select * from user </select> </mapper>
-
注册Mapper
在mybatis-config.xml中添加如下代码
<!--注册Mapper--> <mappers> <mapper class="com/teng/dao/UserMapper.xml"/> </mappers> ?urlSSL=true&useUnicode=true&characterEncoding=UTF-8 ?userSSL=true&useUnicode=true&characterEncoding=UTF-8
-
编写测试类
@Test
public void test(){
//1.获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//2.执行SQL
// 方式一:getMapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user.toString());
}
//关闭sqlSession
sqlSession.close();
}
2.3、可能遇到的问题
1、
无法找到mybatis-config.xml配置文件,一般是你构建SqlSessionFactory时的resource赋值路径错了
2、
org.apache.ibatis.binding.BindingException: Type interface com.kuang.dao.UserMapper is not known to the MapperRegistry.
无法读取到UserMapper.xml文件,默认只读取java后缀的文件
maven由于他的约定大于配置,写的配置文件无法被导出或者生效的问题,解决方案:
<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
3、CRUD
3.1、CRUD
namespace中的包名要和Dao/Mapper接口的包名一致
在UserMapper.xml中:
-
id:就是对应的namespace中的方法名;
-
resultType : Sql语句执行的返回值;
-
parameterType : 参数类型;
具体实现:
UserMapper.java
//根据用户ID查询用户信息
User getUserByID(int id);
//插入一个用户
int addUser(User user);
//修改用户
int updateUser(User user);
//删除用户
int delUser(int id);
UserMapper.xml
<select id="getUserByID" parameterType="int" resultType="com.teng.entity.User">
select * from user where id = #{id}
</select>
<insert id="addUser" parameterType="com.teng.entity.User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="com.teng.entity.User">
update user set name=#{name},pwd=#{pwd} where id=#{id};
</update>
<delete id="delUser" parameterType="int">
delete from user where id = #{id}
</delete>
测试类
@Test
public void test02(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User userByID = userMapper.getUserByID(1);
System.out.println(userByID.toString());
sqlSession.close();;
}
@Test
public void test03(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.addUser(new User(4, "小四", "123456"));
//增删改必须要提交事务
sqlSession.commit();
sqlSession.close();
}
@Test
public void test04(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.updateUser(new User(4, "小六六", "666666"));
sqlSession.commit();
sqlSession.close();
}
@Test
public void test05(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.delUser(4);
sqlSession.commit();
sqlSession.close();
}
注意:增删改操作时需要提交事务
3.2、巧妙使用Map集合
当我们的实体类,或者数据库中的表,字段或者参数过多,我们应该考虑使用Map
//插入一个用户
int addUser1(Map<String,String> map);
<insert id="addUser1" parameterType="Map">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
<!--此处的id、name、pwd可以自定义!-->
</insert>
@Test
public void test06(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, String> hashMap = new HashMap<String, String>();
//此处的key值需要和上面的自定义匹配!
userMapper.ad
hashMap.put("id","4");
hashMap.put("name","王四");
hashMap.put("pwd","123456");dUser1(hashMap);
sqlSession.commit();
sqlSession.close();
}
-
Map传递参数,直接在sql中取出key即可【parameter=“map”】
-
对象传递参数,直接在sql中取出对象的属性即可【parameter=“Object”】
-
只有一个基本类型参数的情况下,可以直接在sql中取到
-
多个参数用Map , 或者注解
3.3、模糊查询
当前数据库user表内容
执行代码:
//根据用户名称查询用户信息
List<User> getUserByName(String name);
<select id="getUserByName" parameterType="String" resultType="com.teng.entity.User">
select * from user where name like "%" #{name} "%"
</select>
@Test
public void test07(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userByName = userMapper.getUserByName("王");
for (User user : userByName) {
System.out.println(user.toString());
}
sqlSession.close();;
}
查询结果:
注意:
至于为什么是在SQL语句中直接写死“%”#{name}“%”,而不是在测试类中使用“%王%”,需要注意SQL注入问题(Sql 注入攻击是通过将恶意的 Sql 查询或添加语句插入到应用的输入参数中,再在后台 Sql 服务器上解析执行进行的攻击,它目前黑客对数据库进行攻击的最常用手段之一。)
4、配置解析
Mybatis的配置文件包含了会深深影响MyBatis行为的设置(setting)和属性(properties)信息
4.1、核心配置
mybatis-config.xml
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
4.2、配置环境
-
MyBatis 可以配置成适应多种环境
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境
-
学会使用配置多套运行环境
-
MyBatis默认的事务管理器就是JDBC ,连接池:POOLED
4.3、属性properties
可以通过properties属性来实现引用配置文件
-
编写一个配置文件:db.properties
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8 username=root password=123456
-
在核心配置文件中引入
<properties resource="db.properties"/> <!--或者使用下面这种方式增加其中一些属性配置--> <properties resource="db.properties"> <property name="username" value="root"/> <property name="password" value="123456"/> </properties>
注意:在使用下面的方式时,如果两个文件中有同一个字段,有限使用外部配置文件的字段
4.4、类型别名typeAliases
-
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置
-
意在降低冗余的全限定类名书写
<!--可以给实体类起别名--> <typeAliases> <typeAlias type="com.kuang.pojo.User" alias="User"/> </typeAliases>
也可以指定一个包,每一个在包
domain.blog
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如domain.blog.Author
的别名为author
,;若有注解,则别名为其注解值。见下面的例子:<typeAliases> <package name="com.kuang.pojo"/> </typeAliases>
一般情况下,在实体类比较少的时候使用第一种方式,在实体类比较多的时候是使用第二种方式,其中:第一种可以DIY别名,第二种不行,如果非要改则需要在实体上增加注解!
@Alias("author") public class Author { ... }
4.5、 设置 Settings
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
主要掌握:
4.6、其它配置
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins 插件
- mybatis-generator-core
- mybatis-plus
- 通用mapper
4.7、映射器 mappers
MapperRegistry:注册绑定我们的Mapper文件;
方式一:【推荐使用】
<!--每一个Mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers>
<mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>
方式二:使用class文件绑定注册
<!--每一个Mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers>
<mapper class="com.kuang.dao.UserMapper"/>
</mappers>
注意点:
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须在同一个包下
方式三:使用包扫描进行注入
<mappers>
<package name="com.kuang.dao"/>
</mappers>
4.8、作用域和生命周期
声明周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题。
- SqlSessionFactoryBuilder:
- 一旦创建了SqlSessionFactory,就不再需要它了
- 局部变量
- SqlSessionFactory:
- 说白了就可以想象为:数据库连接池
- SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建一个实例。
- 因此SqlSessionFactory的最佳作用域是应用作用域(ApplocationContext)。
- 最简单的就是使用单例模式或静态单例模式。
- SqlSession:
- 连接到连接池的一个请求
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
- 用完之后需要赶紧关闭,否则资源被占用!
4.9、解决属性名称和字段名不一致问题
数据库字段
属性名
解决方法:
-
起别名
// select * from user where id = #{id} // 类型处理器 // select id,name,pwd from user where id = #{id} <select id="getUserById" resultType="com.teng.entity.User"> select id,name,pwd as password from USER where id = #{id} </select>
-
使用resultMap结果集
<!--结果集映射--> <resultMap id="UserMap" type="User"> <!--column数据库中的字段,property实体类中的属性--> <result column="id" property="id"></result> <result column="name" property="name"></result> <result column="pwd" property="password"></result> </resultMap> <select id="getUserList" resultMap="UserMap"> select * from USER </select>
-
resultMap 元素是 MyBatis 中最重要最强大的元素
-
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了
-
ResultMap 的优秀之处——你完全可以不用显式地配置它们
5、日志
5.1、日志工厂
设置名:logImpl
设置值:
- SLF4J
- LOG4J 【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING 【掌握】
- NO_LOGGING
具体在Mybatis中使用哪一个日志,需要在setting中进行设定
STDOUT_LOGGING
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
添加前后控制台输出对比:
5.2、log4j
- 什么是log4j?
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件;
- 我们也可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程;
- 最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
-
log4j的使用
先导入log4j的依赖
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
创建log4j.properties配置文件
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/rzp.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sq1.PreparedStatement=DEBUG
配置settings为log4j实现
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
测试
log4j的简单使用:
-
创建对象
//注意导包为:import org.apache.log4j.Logger; static Logger logger = Logger.getLogger(UserMapperTest.class);
-
测试
@Test public void testLog4j(){ logger.info("Info:进入testLog4j"); logger.debug("Debug:进入testLog4j"); logger.error("Error:进入testLog4j"); }
-
结果
6、分页
6.1、使用limit进行分页
SQL语句:
select * from user limit [startIndex],[pageSize]
-
接口
//limit分页查询 List<User> getUserByLimit(Map<String,Integer> map);
-
Mapper.xml
<select id="getUserByLimit" parameterType="map" resultType="User"> select * from user limit #{startIndex},#{pageSize} </select>
-
测试
@Test public void getUserBylimit(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Integer> hashMap = new HashMap<String, Integer>(); hashMap.put("startIndex",1); hashMap.put("pageSize",2); List<User> userByLimit = userMapper.getUserByLimit(hashMap); for (User user : userByLimit) { System.out.println(user.toString()); } sqlSession.close(); }
-
输出结果
6.2、RowBounds实现分页
-
接口
//RowBounds分页查询 List<User> getUserByRowBounds();
-
Mapper.xml
<select id="getUserByRowBounds" resultType="User"> select * from user </select>
-
测试
@Test public void getUserByRowBounds(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); RowBounds rowBounds = new RowBounds(1, 2); //在代码层面实现分页处理 List<User> selectList = sqlSession.selectList("com.teng.dao.UserMapper.getUserByRowBounds", null, rowBounds); for (User user : selectList) { System.out.println(user.toString()); } sqlSession.close(); }
-
输出结果
6.3、PageHelper分页插件
7、使用注解开发
7.1、面向接口开发
三个面向区别
- 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性和方法;
- 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现;
- 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构;
7.2、使用注解开发
-
编写接口
//查询所有用户 @Select("select * from user") List<User> getUserList();
-
注册Mapper
<mappers> <mapper class="com.teng.dao.UserMapper"/> </mappers>
-
测试
@Test public void getUserList(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUserList(); for (User user : userList) { System.out.println(user.toString()); } sqlSession.close(); }
-
测试结果
本质:反射机制实现
底层:动态代理
7.3、注解实现CRUD
可以在工具类中设置自动提交事物
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
-
接口
//增加用户 @Insert("insert into user(id,name,pwd) values (#{id},#{name},#{pwd})") int insertUser(@Param("id") int id, @Param("name") String name, @Param("pwd") String pwd); //查询用户 @Select("select * from user where id = #{id}") User getUserByID(@Param("id") int id); //修改用户 @Update("update user set name = #{name} ,pwd = #{pwd} where id = #{id}") int updateUserByID(@Param("name") String name, @Param("pwd") String pwd, @Param("id") int id); //删除用户 @Delete("delete from user where id = #{id}") int deleteUserByID(@Param("id") int id);
-
测试
//增加用户 @Test public void insertUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.insertUser(5,"小五","123456"); sqlSession.close(); } //查询用户 @Test public void getUserByID(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User userByID = userMapper.getUserByID(5); System.out.println(userByID); sqlSession.close(); } //修改用户 @Test public void updateUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.updateUserByID("小五","111111",5); sqlSession.close(); } //删除用户 @Test public void deleteUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.deleteUserByID(5); sqlSession.close(); }
-
测试结果
关于@Param( )注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但是建议大家都加上
- 我们在SQL中引用的就是我们这里的@Param()中设定的属性名
#{}和${}的区别:
- #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by “111”, 如果传入的值是id,则解析成的sql为order by “id”.
- $将传入的数据直接显示生成在sql中。如:orderby将传入的数据直接显示生成在sql中。如:order by user_id $,如果传入的值是111,那么解析成sql时的值为order by user_id, 如果传入的值是id,则解析成的sql为order by id.
- #方式能够很大程度防止sql注入。
- $方式无法防止Sql注入。
- $方式一般用于传入数据库对象,例如传入表名.
- 一般能用#的就别用$.
MyBatis排序时使用order by 动态参数时需要注意,用$而不是#
8、Lombok
-
什么是Lombok
Lombok是一款Java开发插件,可以通过它定义的注解来精简冗长和繁琐的代码,主要针对简单的Java模型对象(POJO)。
好处就显而易见了,可以节省大量重复工作,特别是当POJO类的属性增减时,需要重复修改的Getter/Setter、构造器方法、equals方法和toString方法等。
而且Lombok针对这些内容的处理是在编译期,而不是通过反射机制,这样的好处是并不会降低系统的性能。
-
Lombok的安装
- 在IDEA的插件配置中搜索Lombok安装
-
添加Maven依赖
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency>
-
使用
@Data//无参构造、get/set方法、equals()、hashCode()、toString() @AllArgsConstructor//有参构造 @NoArgsConstructor//无参构造 public class User { private int id; private String name; private String password; }
-
效果
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
experimental @var
@UtilityClass
@ExtensionMethod (Experimental, activate manually in plugin settings)
9、多对一处理
数据库搭建
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(`id`, `name`) VALUES (1, 梁老师);
INSERT INTO teacher(`id`, `name`) VALUES (2, 胡老师);
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT
INTO `student` (`id`, `name`, `tid`) VALUES (1, 小明, 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (2, 小红, 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (3, 小张, 2);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (4, 小李, 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (5, 小王, 2);
其中:
对于学生而言:几个学生归同一个老师管——>多对一
对于老师而言:一个老师管几个学生——>一对多
9.1、 测试环境搭建
-
导入lombok
-
新建实体类Teacher,Student
//Student @Data public class Student { private int id; private String name; private Teacher teacher; } //Teacher @Data public class Teacher { private int id; private String name; }
-
建立Mapper接口
-
建立Mapper.xml文件
-
在核心配置文件中绑定注册我们的Mapper接口或者文件 【方式很多,随心选】
-
测试查询是否能够成功
9.2、按照查询嵌套处理
<!--
思路:
1. 查询所有的学生信息
2. 根据查询出来的学生的tid寻找特定的老师 (子查询)
-->
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!--复杂的属性,我们需要单独出来 对象:association 集合:collection-->
<collection property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="teacher">
select * from teacher where id = #{id}
</select>
9.3、按照结果嵌套处理
<!--按照结果进行查询-->
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid , s.name sname, t.name tname
from student s,teacher t
where s.tid=t.id
</select>
<!--结果封装,将查询出来的列封装到对象属性中-->
<resultMap id="StudentTeacher2" type="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
回顾Mysql多对一查询方式:
- 子查询 (按照查询嵌套)
- 联表查询 (按照结果嵌套)
10、一对多处理
10.1、环境搭建
-
导入lombok
-
新建实体类Teacher,Student
//Student @Data public class Student { private int id; private String name; private int tid; } //Teacher @Data public class Teacher { private int id; private String name; private List<Student> student; }
-
建立Mapper接口
-
建立Mapper.xml文件
-
在核心配置文件中绑定注册我们的Mapper接口或者文件 【方式很多,随心选】
-
测试查询是否能够成功
10.2、按照结果嵌套处理
<select id="getTeacherList1" resultMap="teacherMap">
select t.id as tid, t.name as tname, s.id as sid, s.name as sname
from teacher t, student s
where t.id = s.tid and t.id = #{id}
</select>
<resultMap id="teacherMap" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="student" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="tname"/>
</collection>
</resultMap>
11、小结
- 关联 – association 【多对一】
- 集合 – collection 【一对多】
- javaType & ofType
- JavaType用来指定实体类中的类型
- ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
注意点:
- 保证SQL的可读性,尽量保证通俗易懂
- 注意一对多和多对一,属性名和字段的问题
- 如果问题不好排查错误,可以使用日志,建议使用Log4j
面试高频
- Mysql引擎
- InnoDB底层原理
- 索引
- 索引优化
12、动态SQL
什么是动态SQL:动态SQL就是根据不同的条件生成不同的SQL语句
所谓的动态SQL,本质上还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
准备数据库:
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT 博客id,
`title` VARCHAR(100) NOT NULL COMMENT 博客标题,
`author` VARCHAR(30) NOT NULL COMMENT 博客作者,
`create_time` DATETIME NOT NULL COMMENT 创建时间,
`views` INT(30) NOT NULL COMMENT 浏览量
)ENGINE=INNODB DEFAULT CHARSET=utf8
12.1、搭建环境
创建一个子工程
-
导包
-
编写配置文件
-
编写实体类
@Data public class Blog { private String id; private String title; private String author; private Date createTime;//此字段与数据库中的字段不一致,需要在配置文件中配置mapUnderscoreToCamelCase为true private int views; }
-
编写实体类对应Mapper接口和Mapper.xml文件
-
增加一个生成ID的类
public class IDutils { public static String getID(){ return UUID.randomUUID().toString().replace("-",""); } }
-
插入数据
<mapper namespace="com.teng.dao.BlogMapper"> <insert id="addBlog" parameterType="blog"> insert into blog (id,title,author,create_time,views) values (#{id},#{title},#{author},#{createTime},#{views}); </insert> </mapper>
@org.junit.Test public void addInitBlog(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class); Blog blog = new Blog(); blog.setId(IDutils.getID()); blog.setTitle("Mybatis如此简单!"); blog.setAuthor("小梁"); blog.setCreateTime(new Date()); blog.setViews(9999); blogMapper.addBlog(blog); blog.setId(IDutils.getID()); blog.setTitle("Spring如此简单!"); blogMapper.addBlog(blog); blog.setId(IDutils.getID()); blog.setTitle("SpringMVC如此简单!"); blogMapper.addBlog(blog); blog.setId(IDutils.getID()); blog.setTitle("微服务如此简单!"); blogMapper.addBlog(blog); sqlSession.close(); }
12.1、if
-
接口
//查询指定要求的blog List<Blog> queryBlog(Map map);
-
BlogMapper.xml
<select id="queryBlog" parameterType="map" resultType="blog"> select * from blog where 1=1 <if test="title != null"> and title = #{title} </if> <if test="author != null"> and author = #{author} </if> </select> <!--其中的 where 1=1 不规范,此时需要进行优化--> <select id="queryBlog" parameterType="map" resultType="blog"> select * from blog <where> <if test="title != null"> and title = #{title} </if> <if test="author != null"> and author = #{author} </if> </where> </select>
-
测试
@org.junit.Test public void queryBlog(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap<String, String> hashMap = new HashMap<String, String>(); hashMap.put("title","Mybatis如此简单!"); // hashMap.put("author","小梁"); List<Blog> blogs = mapper.queryBlog(hashMap); for (Blog blog : blogs) { System.out.println(blog); } }
-
测试结果
12.2、choose, when, otherwise
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null"> title=#{title}</when>
<when test="author != null"> author=#{author}</when>
<!--
还可以设定一个otherwise:如果上面两个条件都不走,则走otherwise
比如:<otherwise> id=#{id}</otherwise>
-->
</choose>
</where>
</select>
12.3、set
//修改字段名
int updateBlog(Map map);
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title!=null">title=#{title}</if>
<if test="author!=null">author=#{author}</if>
</set>
where id = #{id}
</update>
@org.junit.Test
public void updateBlog(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String,String> hashMap = new HashMap<String,String>();
hashMap.put("id","208f88df984f43d380ab8beef41c9743");
hashMap.put("title","SpringMVC真的简单吗?");
blogMapper.updateBlog(hashMap);
sqlSession.close();
}
12.4、trim
mybatis的trim标签一般用于去除sql语句中多余的and关键字,逗号,或者给sql语句前拼接 “where“、“set“以及“values(“ 等前缀,或者添加“)“等后缀,可用于选择性插入、更新、删除或者条件查询等操作。
属性 | 描述 |
---|---|
prefix | 给sql语句拼接的前缀 |
suffix | 给sql语句拼接的后缀 |
prefixOverrides | 去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为”AND”,当sql语句的开头为”AND”,trim标签将会去除该”AND” |
suffixOverrides | 去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定 |
实例:
<select id="queryBlog" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
<!--
相当于SQL拼接前缀为where(prefix="where"),去除sql语句前面的关键字或者字符and(prefixOverrides="and")
具体如下所示
-->
<select id="queryBlog" parameterType="map" resultType="blog">
select * from blog
<trim prefix="where" prefixOverrides="and">
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</trim>
</select>
12.5、foreach
mybatis的foreach标签经常用于遍历集合,构建in条件语句或者批量操作语句
属性 | 描述 |
---|---|
collection | 表示迭代集合的名称,可以使用@Param注解指定 |
item | 表示本次迭代获取的元素,若collection为List、Set或者数组,则表示其中的元素;若collection为map,则代表key-value的value,该参数为必选 |
open | 表示该语句以什么开始,最常用的是左括弧’(’,注意:mybatis会将该字符拼接到整体的sql语句之前,并且只拼接一次,该参数为可选项 |
close | 表示该语句以什么结束,最常用的是右括弧’)’,注意:mybatis会将该字符拼接到整体的sql语句之后,该参数为可选项 |
separator | mybatis会在每次迭代后给sql语句append上separator属性指定的字符,该参数为可选项 |
index | 在list、Set和数组中,index表示当前迭代的位置,在map中,index代指是元素的key,该参数是可选项 |
测试:
<select id="queryBlogByID" parameterType="map" resultType="blog">
select * from blog
<where>
<foreach collection="ids" open="(" item="id" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
@org.junit.Test
public void queryBlogByID(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
ArrayList<String> ids = new ArrayList<String>();
ids.add("1");
HashMap map = new HashMap();
map.put("ids",ids);
blogMapper.queryBlogByID(map);
sqlSession.close();
}
12.6、SQL片段
-
使用SQL标签抽取公共部分可
<sql id="if-title-author"> <if test="title!=null"> title = #{title} </if> <if test="author!=null"> and author = #{author} </if> </sql>
-
在需要使用的地方使用Include标签引用即可
<select id="queryBlogIF" parameterType="map" resultType="blog"> select * from blog <where> <include refid="if-title-author"></include> </where> </select>
注意事项:
- 最好基于单标来定义SQL片段
- 不要存在where标签
缓存
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/147371.html