如何获取Mybatis
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
持久化是将程序数据在持久状态和瞬时状态间转换的机制。
为什么需要持久化服务呢?那是由于内存本身的缺陷引起的
什么是持久层?
思路流程:搭建环境–>导入Mybatis—>编写代码—>测试
搭建数据库
CREATE DATABASE `mybatis` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
USE `mybatis`;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` INT(20) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`pwd` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
INSERT INTO `user`(`id`,`name`,`pwd`) VALUES (1,'SolitudeAlma','123456'),(2,'张三','abcdef'),(3,'李四','987654');
新建项目
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.24version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
dependencies>
这样子项目就不需要重复添加依赖
查看帮助文档
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?useSSL=true&useUnicode=true&characterEncoding=UTF-8&"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
configuration>
package com.SolitudeAlma.utils;
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 javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
/**
* sqlSessionFactory ---> sqlSession
* @author SolitudeAlma
* @date 2021/8/13
*/
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL
* 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的SQL语句。 sqlSession相当于jdbc中的
* prepareStatement对象
* @return SqlSessionFactory
*/
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
package com.SolitudeAlma.pojo;
/**
* @author SolitudeAlma
* @date 2021/8/14
* @description 实体类
*/
public class User {
private int id;
private String name;
private String 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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import java.util.List;
/**
* @author SolitudeAlma
* @date 2021/8/14
*/
public interface UserDao {
/**
* 查询所有用户信息
* @return User
*/
List<User> getUserList();
}
用xml代替UserDaoImpl实现类
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.SolitudeAlma.dao.UserDao">
<select id="getUserList" resultType="com.SolitudeAlma.pojo.User">
select * from user;
select>
mapper>
注意点:org.apache.ibatis.binding.BindingException: Type interface com.SolitudeAlma.dao.UserDao is not known to the MapperRegistry.
MapperRegistry是什么?
核心配置文件中注册mapper
<mappers>
<mapper resource="com/SolitudeAlma/dao/UserMapper.xml"/>
mappers>
Could not find resource com/SolitudeAlma/dao/UserMapper.xml
JavaWeb中提到过,项目构建时,java目录下的非java文件不会被生成到target目录,所以我们需要在父项目的pom.xml中build标签下加入rusource标签,把我们的xml等配置文件也生成到target
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import com.SolitudeAlma.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* junit测试类
* @author SolitudeAlma
* @date 2021/8/14
*/
public class UserDaoTest {
@Test
public void test() {
//获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:getMapper
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
//方式二:不推荐使用
//List userList = sqlSession.selectList("com.SolitudeAlma.dao.UserDao.getUserList");
for (User user : userList) {
System.out.println(user);
}
//关闭SqlSession
sqlSession.close();
}
}
可能遇到的问题:
namespace
结论:
配置文件中namespace中的名称为对应Mapper接口或者Dao接口的完整包名,必须一致!
需求:根据id查询用户
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import java.util.List;
/**
* @author SolitudeAlma
* @date 2021/8/14
*/
public interface UserDao {
/**
* 查询所有用户信息
* @return List 用户信息集合
*/
List<User> getUserList();
/**
* 根据id查询用户信息
* @param id 用户唯一标识
* @return User 单个用户的信息
*/
User getUserById(int id);
}
<select id="getUserById" resultType="com.SolitudeAlma.pojo.User" parameterType="int">
select * from user where id = #{id};
select>
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import com.SolitudeAlma.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* junit测试类
* @author SolitudeAlma
* @date 2021/8/14
*/
public class UserDaoTest {
@Test
public void test() {
//获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:getMapper
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
//方式二:不推荐使用
//List userList = sqlSession.selectList("com.SolitudeAlma.dao.UserDao.getUserList");
for (User user : userList) {
System.out.println(user);
}
//关闭SqlSession
sqlSession.close();
}
@Test
public void getUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
User userInfo = mapper.getUserById(1);
System.out.println(userInfo);
sqlSession.close();
}
}
一般使用insert标签进行插入操作,它的配置和select标签差不多!
需求:给数据库增加一个用户
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import java.util.List;
/**
* @author SolitudeAlma
* @date 2021/8/14
*/
public interface UserDao {
/**
* 查询所有用户信息
* @return List 用户信息集合
*/
List<User> getUserList();
/**
* 根据id查询用户信息
* @param id 用户唯一标识
* @return User 单个用户的信息
*/
User getUserById(int id);
/**
* 插入一个用户信息
* @param user 用户信息类
* @return SQL查询结果,成功影响几条数据
*/
int insertOne(User user);
}
<insert id="insertOne" parameterType="com.SolitudeAlma.pojo.User" >
insert into user (id, name, pwd) values (#{id}, #{name}, #{pwd});
insert>
3.测试
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import com.SolitudeAlma.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* junit测试类
* @author SolitudeAlma
* @date 2021/8/14
*/
public class UserDaoTest {
@Test
public void test() {
//获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:getMapper
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
//方式二:不推荐使用
//List userList = sqlSession.selectList("com.SolitudeAlma.dao.UserDao.getUserList");
for (User user : userList) {
System.out.println(user);
}
//关闭SqlSession
sqlSession.close();
}
@Test
public void getUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
User userInfo = mapper.getUserById(1);
System.out.println(userInfo);
sqlSession.close();
}
@Test
public void insertOne() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int result = mapper.insertOne(new User(4, "小明", "1212"));
System.out.println(result);
//如果不加上这句提交事务,我们会发现虽然输出了1,但是还是没有插入成功,那是因为我们的工
//具类中的openSession方法没有传参数,默认手动提交事务,而mysql是自动提交事务的,所以我
//只要设置参数为true就行了,false和不传参都是手动提交。这样相对比较安全
sqlSession.commit();
sqlSession.close();
}
}
注意点:增、删、改操作需要提交事务!
一般使用update标签进行更新操作,它的配置和select标签差不多!
需求:修改用户的信息
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import java.util.List;
/**
* @author SolitudeAlma
* @date 2021/8/14
*/
public interface UserDao {
/**
* 查询所有用户信息
* @return List 用户信息集合
*/
List<User> getUserList();
/**
* 根据id查询用户信息
* @param id 用户唯一标识
* @return User 单个用户的信息
*/
User getUserById(int id);
/**
* 插入一个用户信息
* @param user 用户信息类
* @return SQL查询结果,成功影响几条数据
*/
int insertOne(User user);
/**
* 修改一个用户的信息
* @param user 用户信息类
* @return SQL查询结果,成功影响几条数据
*/
int updateOne(User user);
}
<update id="updateOne" parameterType="com.SolitudeAlma.pojo.User">
update user set name = #{name}, pwd = #{pwd} where id = #{id};
update>
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import com.SolitudeAlma.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* junit测试类
* @author SolitudeAlma
* @date 2021/8/14
*/
public class UserDaoTest {
@Test
public void test() {
//获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:getMapper
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
//方式二:不推荐使用
//List userList = sqlSession.selectList("com.SolitudeAlma.dao.UserDao.getUserList");
for (User user : userList) {
System.out.println(user);
}
//关闭SqlSession
sqlSession.close();
}
@Test
public void getUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
User userInfo = mapper.getUserById(1);
System.out.println(userInfo);
sqlSession.close();
}
@Test
public void insertOne() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int result = mapper.insertOne(new User(4, "小明", "1212"));
System.out.println(result);
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateOne() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int result = mapper.updateOne(new User(4, "废物", "12121"));
System.out.println(result);
sqlSession.commit();
sqlSession.close();
}
}
一般使用delete标签进行删除操作,它的配置和select标签差不多!
需求:根据id删除一个用户
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import java.util.List;
/**
* @author SolitudeAlma
* @date 2021/8/14
*/
public interface UserDao {
/**
* 查询所有用户信息
* @return List 用户信息集合
*/
List<User> getUserList();
/**
* 根据id查询用户信息
* @param id 用户唯一标识
* @return User 单个用户的信息
*/
User getUserById(int id);
/**
* 插入一个用户信息
* @param user 用户信息类
* @return SQL查询结果,成功影响几条数据
*/
int insertOne(User user);
/**
* 修改一个用户的信息
* @param user 用户信息类
* @return SQL查询结果,成功影响几条数据
*/
int updateOne(User user);
/**
* 删除一个用户的信息
* @param id 用户唯一标识
* @return SQL查询结果,成功影响几条数据
*/
int deleteById(int id);
}
<delete id="deleteById" parameterType="int">
delete from user where id = #{id};
delete>
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import com.SolitudeAlma.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* junit测试类
* @author SolitudeAlma
* @date 2021/8/14
*/
public class UserDaoTest {
@Test
public void test() {
//获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:getMapper
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
//方式二:不推荐使用
//List userList = sqlSession.selectList("com.SolitudeAlma.dao.UserDao.getUserList");
for (User user : userList) {
System.out.println(user);
}
//关闭SqlSession
sqlSession.close();
}
@Test
public void getUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
User userInfo = mapper.getUserById(1);
System.out.println(userInfo);
sqlSession.close();
}
@Test
public void insertOne() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int result = mapper.insertOne(new User(4, "小明", "1212"));
System.out.println(result);
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateOne() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int result = mapper.updateOne(new User(4, "废物", "12121"));
System.out.println(result);
sqlSession.commit();
sqlSession.close();
}
@Test
public void deleteOne() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int result = mapper.deleteById(4);
System.out.println(result);
sqlSession.commit();
sqlSession.close();
}
}
小结:
模糊查询like语句该怎么写?
第1种:在Java代码中添加sql通配符。
string wildcardname = "%smi%";
List<name> names = mapper.selectlike(wildcardname);
<select id="selectlike">
select * from foo where bar like #{value}
</select>
第2种:在sql语句中拼接通配符,会引起sql注入
string wildcardname = "smi";
List<name> names = mapper.selectlike(wildcardname);
<select id="selectlike">
select * from foo where bar like "%"${value}"%"
</select>
在mybatis配置文件中:
不推荐使用${},能用#{}就用#{},也可以使用MySQL函数concat连接字符串。
固定的值可以用${},因为本来就是拼接字符串,但动态的不能
https://www.cnblogs.com/xiao-lin-unit/p/13644004.html
https://blog.csdn.net/Mr_Qiao93/article/details/109168291
https://blog.csdn.net/weixin_42452395/article/details/113869915
sql语句需要的参数很多时,可以考虑使用map而不是用类。
map可以随意定义key的名字,灵活性高,而类需要定义属性,set、get方法等,修改名字得修改属性名,麻烦
大致用法:
/**
* 插入过个用户信息
* @param map 用户信息集合
* @return SQL查询结果,成功影响几条数据
*/
int insertMany(Map<String, Object> map);
<insert id="insertMany" parameterType="map" >
insert into user (id, name, pwd) values (#{userId}, #{userName}, #{password});
insert>
@Test
public void insertMany() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map <String, Object> map = new HashMap<>();
map.put("userId", 5);
map.put("userName", "小红");
map.put("password", "23333");
int result = mapper.insertMany(map);
System.out.println(result);
sqlSession.commit();
sqlSession.close();
}
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
<!-- 注意元素节点的顺序!顺序不对会报错 -->
可以阅读 mybatis-config.xml 上面的dtd的头文件
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
<transactionManager type="[ JDBC | MANAGED ]"/>
数据库这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。具体看官方文档
我们来优化配置文件
第一步 ; 在资源目录下新建一个db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
user=root
password=xxx
第二步 : 将文件导入properties 配置文件
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="xxx"/>
properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="com/SolitudeAlma/dao/UserMapper.xml"/>
mappers>
configuration>
注意:严格按照标签顺序来写
更多操作,可以查看官方文档
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
<typeAliases>
<typeAlias type="com.SolitudeAlma.pojo.User" alias="User"/>
typeAliases>
当这样配置时,User可以用在任何使用com.SolitudeAlma.pojo.User的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="com.SolitudeAlma.pojo"/>
typeAliases>
每一个在com.SolitudeAlma.pojo包中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
大小写都可以识别,但是用小写的话就知道你是用了扫描包这个方法
若有注解,则别名为其注解值。见下面的例子:
@Alias("user")
public class User {
...
}
实体类比较少的时候用第一钟方法,比较多的时候直接扫描包
第一种方法可以自定义别名,第二种不行,但可以用注解实现自定义
去官网查看一下Mybatis默认的一些类型别名(基本数据类型和引用类型等等)
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
settings>
mappers
引入资源方式
<mappers>
<mapper resource="com/SolitudeAlma/dao/UserMapper.xml"/>
mappers>
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
mappers>
<mappers>
<mapper class="com.SolitudeAlma.dao.UserDao"/>
mappers>
<mappers>
<package name="com.SolitudeAlma.dao"/>
mappers>
Mapper配置文件
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kuang.mapper.UserMapper">
mapper>
MyBatis 的真正强大在于它的映射语句,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 为聚焦于 SQL 而构建,以尽可能地为你减少麻烦。
typeHandlers类型处理器
objectFactory对象工厂
plugins插件
作用域(Scope)和生命周期
理解我们目前已经讨论过的不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。
我们可以先画一个流程图,分析一下Mybatis的执行过程!
作用域理解
要解决的问题:属性名和字段名不一致
环境:新建一个项目,将之前的项目拷贝过来
1、查看之前的数据库的字段名
2、Java中的实体类设计
package com.SolitudeAlma.pojo;
import org.apache.ibatis.type.Alias;
/**
* @author SolitudeAlma
* @date 2021/8/15
* @description 实体类
*/
@Alias("user")
public class User {
private int id;
private String name;
private String password;
public User() {
}
public User(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
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 getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
3、接口
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import java.util.List;
/**
* @author SolitudeAlma
* @date 2021/8/15
*/
public interface UserDao {
/**
* 根据id查询用户信息
* @param id 用户唯一标识
* @return User 单个用户的信息
*/
User getUserById(int id);
}
4、mapper映射文件
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.SolitudeAlma.dao.UserDao">
<select id="getUserById" resultType="user" parameterType="int">
select * from user where id = #{id};
select>
mapper>
5、测试
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import com.SolitudeAlma.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* junit测试类
* @author SolitudeAlma
* @date 2021/8/15
*/
public class UserDaoTest {
@Test
public void getUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
User userInfo = mapper.getUserById(1);
System.out.println(userInfo);
sqlSession.close();
}
}
结果:
分析:
方案一:为列名指定别名 , 别名和java实体类的属性名一致。
<select id="selectUserById" resultType="User">
select id , name , pwd as password from user where id = #{id}
select>
方案二:使用结果集映射->ResultMap 【推荐】
<resultMap id="UserMap" type="User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
resultMap>
<select id="selectUserById" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
select>
自动映射
你已经见过简单映射语句的示例了,但并没有显式指定resultMap。比如:
<select id="selectUserById" resultType="map">
select id , name , pwd
from user
where id = #{id}
select>
上述语句只是简单地将所有的列映射到HashMap的键上,这由resultType属性指定。虽然在大部分情况下都够用,但是 HashMap 不是一个很好的模型。你的程序更可能会使用 JavaBean 或 POJO(Plain Old Java Objects,普通老式 Java 对象)作为模型。
ResultMap最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。
1、返回值类型为resultMap
<select id="selectUserById" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
select>
2、编写resultMap,实现手动映射!
<resultMap id="UserMap" type="User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
resultMap>
如果世界总是这么简单就好了。但是肯定不是的,数据库中,存在一对多,多对一的情况,我们之后会使用到一些高级的结果集映射,association,collection这些。
思考:在测试SQL的时候,要是能够在控制台输出 SQL 的话,是不是就能够有更快的排错效率?
如果一个 数据库相关的操作出现了问题,我们可以根据输出的SQL语句快速排查问题。
对于以往的开发过程,我们会经常使用到debug模式来调节,跟踪我们的代码执行过程。但是现在使用Mybatis是基于接口,配置文件的源代码执行过程。因此,我们必须选择日志工具来作为我们开发,调节程序的工具。
Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:
具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按SLF4J、Apache Commons Logging、Log4j 2、Log4j、JDK logging列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。
标准日志实现
指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
测试,可以看到控制台有大量的输出!我们可以通过这些输出来判断程序到底哪里出了Bug
简介:
使用步骤:
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
#将等级为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/Mybatis-04.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.sql.PreparedStatement=DEBUG
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import com.SolitudeAlma.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
/**
* junit测试类
* @author SolitudeAlma
* @date 2021/8/15
*/
public class UserDaoTest {
//注意导包:org.apache.log4j.Logger
static Logger logger = Logger.getLogger(UserDaoTest.class);
@Test
public void getUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
User userInfo = mapper.getUserById(1);
System.out.println(userInfo);
sqlSession.close();
}
@Test
public void selectUser() {
logger.info("info:进入selectUser方法");
logger.debug("debug:进入selectUser方法");
logger.error("error: 进入selectUser方法");
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
User userInfo = mapper.getUserById(1);
System.out.println(userInfo);
sqlSession.close();
}
}
需要导包,参数为当前类的class对象
https://www.cnblogs.com/xiaobaizhiqian/p/7956690.html
https://blog.csdn.net/eagleuniversityeye/article/details/80582140
https://www.cnblogs.com/wangzhuxing/p/7753420.html
思考:为什么需要分页?
在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。
#语法
SELECT * FROM table LIMIT stratIndex,pageSize
SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15
#为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:
SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.
#如果只给定一个参数,它表示返回最大的记录行数目:
SELECT * FROM table LIMIT 5; //检索前 5 个记录行
#换句话说,LIMIT n 等价于 LIMIT 0,n。
步骤:
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from user limit #{currentPage},#{pageSize};
select>
//选择全部用户实现分页
List<User> selectUser(Map<String,Integer> map);
//分页查询 , 两个参数startIndex , pageSize
@Test
public void getUserByLimit() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int currentPage = 1;
int pageSize = 2;
Map<String, Integer> map = new HashMap<>();
map.put("currentPage", (currentPage - 1) * pageSize);//下标从0开始
map.put("pageSize", pageSize);
List<User> userList = mapper.getUserByLimit(map);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
我们除了使用Limit在SQL层面实现分页,也可以使用RowBounds在Java代码层面实现分页,当然此种方式作为了解即可。我们来看下如何实现的!
步骤:
/**
* 通过Java类实现分页
* @return List 用户信息集合
*/
List<User> getUserByRowBounds();
<select id="getUserByRowBounds" resultType="user">
select * from user
select>
@Test
public void getUserByRowBounds() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int currentPage = 1;
int pageSize = 2;
RowBounds rowBounds = new RowBounds(currentPage - 1, pageSize);
//通过Java层代码实现分页
//通过session.**方法进行传递rowBounds,[此种方式现在已经不推荐使用了]
List<User> userList = sqlSession.selectList("com.SolitudeAlma.dao.UserDao.getUserByRowBounds", null, rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
了解即可,可以自己尝试使用
官方文档:https://pagehelper.github.io/
在MyBatisPlu中,我们也讲解到了分页实现,所以实现方式很多,看自己的理解合熟练程度进行掌握即可
面向接口编程:
关于接口的理解
三个面向区别
**注意:**利用注解开发就不需要mapper.xml映射文件了 .
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
/**
* @author SolitudeAlma
* @date 2021/8/17
*/
public interface UserDao {
/**
* 获取所用用户信息
* @return List 用户信息集合
*/
@Select("select id, name, pwd as password from user")
List<User> selectAll();
}
<mappers>
<mapper class="com.SolitudeAlma.dao.UserDao"/>
mappers>
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.User;
import com.SolitudeAlma.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* junit测试类
* @author SolitudeAlma
* @date 2021/8/16
*/
public class UserDaoTest {
@Test
public void selectAll() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
//底层主要应用反射
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.selectAll();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
}
改造MybatisUtils工具类的getSession( ) 方法,重载实现。
/**
* 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL
* 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的SQL语句。 sqlSession相当于jdbc中的
* prepareStatement对象
* @return SqlSessionFactory
*/
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true);
}
【注意】确保实体类和数据库字段对应
查询:
/**
* 根据id查询用户信息
* @param id 用户唯一标识 @Param("xxx") 方法存在多个参数时前面必须加上这个注解,也起到起别名的作用
* @return User类 用户信息类
*/
@Select("select id, name, pwd as password from user where id=#{id}")
User selectById(@Param("id") int id);
@Test
public void selectById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
User user = mapper.selectById(1);
System.out.println(user);
sqlSession.close();
}
新增:
/**
* 插入一条用户信息
* @param user 用户信息类
* @return SQL查询结果,影响了几行
*/
@Insert("insert into user(`id`, `name`, `pwd`) values(#{id}, #{name}, #{password})")
int insertOne(User user);
@Test
public void insertOne() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int user = mapper.insertOne(new User(7, "asd", "asdsa"));
System.out.println(user);
sqlSession.close();
}
修改:
/**
* 修改一条用户信息
* @param user 用户信息类
* @return SQL查询结果,影响了几行
*/
@Update("update user set name = #{name}, pwd = #{password} where id = #{id}")
int updateOne(User user);
@Test
public void updateOne() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int user = mapper.updateOne(new User(7, "小王", "145236"));
System.out.println(user);
sqlSession.close();
}
删除:
/**
* 删除一条用户信息
* @param id 用户唯一标识
* @return SQL查询结果,影响了几行
*/
@Delete("delete from user where id = #{uid}")
int deleteOne(@Param("uid") int id);
@Test
public void deleteOne() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
int user = mapper.deleteOne(7);
System.out.println(user);
sqlSession.close();
}
【注意点:增删改一定记得对事务的处理】
关于@param
@Param注解用于给方法参数起一个名字。以下是总结的使用原则:
https://www.cnblogs.com/zhuhui-site/p/10088369.html
https://blog.csdn.net/sinat_33010325/article/details/84261662
#{}与${}的区别
INSERT INTO user (name) VALUES (#{name});
INSERT INTO user (name) VALUES (?);
INSERT INTO user (name) VALUES ('${name}');
INSERT INTO user (name) VALUES ('kuangshen');
使用注解和配置文件协同开发,才是MyBatis的最佳实践
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
使用步骤:
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.20version>
<scope>providedscope>
dependency>
@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
//点开左边的structure查看类结构
@Data:无参构造、get、set、toString()、hashcode()、equals()
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode
@Getter 可以放在固定的字段或者直接放在类上
多对一的理解:
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
INSERT INTO teacher(`id`, `name`) VALUES (1, 'SolitudeAlma');
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=utf8mb4;
INSERT 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', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.20version>
<scope>providedscope>
dependency>
package com.SolitudeAlma.pojo;
import lombok.Data;
/**
* @author SolitudeAlma
* @date 2021/8/18
*/
@Data
public class Teacher {
private int id;
private String name;
}
package com.SolitudeAlma.pojo;
import lombok.Data;
/**
* @author SolitudeAlma
* @date 2021/8/18
*/
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
package com.SolitudeAlma.dao;
/**
* @author SolitudeAlma
* @date 2021/8/18
*/
public interface StudentMapper {
}
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* @author SolitudeAlma
* @date 2021/8/18
*/
public interface TeacherMapper {
/**
* 查询单个老师的信息
* @param id 老师的唯一标识
* @return Teacher 老师信息类
*/
@Select("select * from teacher where id = #{tid}")
Teacher getTeacher(@Param("tid") int id);
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.SolitudeAlma.dao.StudentMapper">
mapper>
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.SolitudeAlma.dao.TeacherMapper">
mapper>
import com.SolitudeAlma.dao.TeacherMapper;
import com.SolitudeAlma.pojo.Teacher;
import com.SolitudeAlma.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
/**
* @author SolitudeAlma
* @date 2021/8/18
*/
public class MyTest {
@Test
public void selectOne() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher.getId() + " " + teacher.getName());
sqlSession.close();
}
}
/**
* 查询所有学生的信息,以及对应老师的信息
* @return List 学生信息集合
*/
List<Student> getStudent();
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kuang.mapper.StudentMapper">
<select id="getStudents" resultMap="studentInfo">
select * from student
select>
<resultMap id="studentInfo" type="Student">
<id property="id" column="id"/>
<id property="name" column="name"/>
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
resultMap>
<select id="getTeacher" resultType="Teacher">
select id, name from teacher where id = #{id}
select>
mapper>
<resultMap id="studentInfo" type="Student">
<association property="teacher" column="{id=tid}" javaType="Teacher" select="getTeacher"/>
resultMap>
<select id="getTeacher" resultType="teacher">
select * from teacher where id = #{id}
select>
import com.SolitudeAlma.dao.StudentMapper;
import com.SolitudeAlma.dao.TeacherMapper;
import com.SolitudeAlma.pojo.Student;
import com.SolitudeAlma.pojo.Teacher;
import com.SolitudeAlma.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* @author SolitudeAlma
* @date 2021/8/18
*/
public class MyTest {
@Test
public void selectOne() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher.getId() + " " + teacher.getName());
sqlSession.close();
}
@Test
public void selectAll() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.getStudent();
for (Student student : students) {
System.out.println("学生:" + student.getName() + "\t" + "老师:" + student.getTeacher().getName());
}
sqlSession.close();
}
}
除了上面这种方式,还有其他思路吗?
我们还可以按照结果进行嵌套处理;
/**
* 查询所有学生的信息,以及对应老师的信息
* @return List 学生信息集合
*/
List<Student> getStudent01();
<select id="getStudent01" resultMap="studentInfo01">
select s.id, s.name, t.name from student as s, teacher as t where s.tid = t.id
select>
<resultMap id="studentInfo01" type="Student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="name"/>
association>
resultMap>
@Test
public void selectAll01() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.getStudent01();
for (Student student : students) {
System.out.println("学生:" + student.getName() + "\t" + "老师:" + student.getTeacher().getName());
}
sqlSession.close();
}
小结:
按照查询进行嵌套处理就像SQL中的子查询
按照结果进行嵌套处理就像SQL中的联表查询
一对多的理解:
不修改
把上一个项目拷过来,重新创建一个module
实体类:
package com.SolitudeAlma.pojo;
import lombok.Data;
import java.util.List;
/**
* @author SolitudeAlma
* @date 2021/8/18
*/
@Data
public class Teacher {
private int id;
private String name;
private List<Student> studentList;
}
package com.SolitudeAlma.pojo;
import lombok.Data;
/**
* @author SolitudeAlma
* @date 2021/8/18
*/
@Data
public class Student {
private int id;
private String name;
private int tid;
}
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.Teacher;
import lombok.Data;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @author SolitudeAlma
* @date 2021/8/18
*/
public interface TeacherMapper {
/**
* 获取指定教师信息的同时获取其教的学生的信息
* @param id 教师唯一标识
* @return List 信息集合,包括教师及所教学生信息
*/
List<Teacher> getTeacher(@Param("tid") int id);
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.SolitudeAlma.dao.TeacherMapper">
<select id="getTeacher" resultMap="teacherInfo">
select s.id as sid, s.name as sname, t.id as tid, t.name as tname from student as s, teacher as t where s.tid = t.id and t.id = #{tid};
select>
<resultMap id="teacherInfo" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="studentList" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
collection >
resultMap>
mapper>
<mappers>
<mapper resource="com/SolitudeAlma/dao/StudentMapper.xml"/>
<mapper resource="com/SolitudeAlma/dao/TeacherMapper.xml"/>
mappers>
import com.SolitudeAlma.dao.TeacherMapper;
import com.SolitudeAlma.pojo.Teacher;
import com.SolitudeAlma.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* @author SolitudeAlma
* @date 2021/8/18
*/
public class MyTest {
@Test
public void getTeacher() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> teacherList = mapper.getTeacher(1);
for (Teacher teacher : teacherList) {
System.out.println("teacher: " + teacher.getName());
teacher.getStudentList().forEach(e -> {
System.out.println("student" + e.getId() + ": name: " + e.getName() + " tid: " + e.getTid());
});
}
}
}
/**
* 获取指定教师信息的同时获取其教的学生的信息
* @param id 教师唯一标识
* @return List 信息集合,包括教师及所教学生信息
*/
List<Teacher> getTeacher01(@Param("tid") int id);
<select id="getTeacher01" resultMap="teacherInfo01">
select id, name from teacher
where id = #{tid};
select>
<resultMap id="teacherInfo01" type="Teacher">
<result property="id" column="id"/>
<collection property="studentList" javaType="List" column="id" ofType="Student" select="getStudentByTeacherId"/>
resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select id, name, tid from student
where tid = #{tid};
select>
@Test
public void getTeacher01() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> teacherList = mapper.getTeacher01(1);
for (Teacher teacher : teacherList) {
System.out.println("teacher: name: " + teacher.getName() + " id: " + teacher.getId());
teacher.getStudentList().forEach(e -> {
System.out.println("student" + e.getId() + ": name: " + e.getName() + " tid: " + e.getTid());
});
}
}
小结:
注意说明:
什么是动态SQL:动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句.
官网描述:
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。
动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。
我们之前写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。
那么怎么去解决这个问题呢?这就要使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。
新建一个数据库表:blog
字段:id,title,author,create_time,views
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=utf8mb4;
package com.SolitudeAlma.utils;
import org.junit.Test;
import java.util.UUID;
/**
* 如果想在非test目录下测试,把依赖的作用域改一下或者去掉
* @author SolitudeAlma
* @date 2021/8/19
*/
public class IDUtils {
public static String getId() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
}
package com.SolitudeAlma.pojo;
import lombok.Data;
import java.util.Date;
/**
* @author SolitudeAlma
* @date 2021/8/19
*/
@Data
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
package com.SolitudeAlma.dao;
/**
* @author SolitudeAlma
* @date 2021/8/19
*/
public interface BlogMapper {
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.SolitudeAlma.dao.BlogMapper">
mapper>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
<mappers>
<mapper resource="com/SolitudeAlma/dao/BlogMapper.xml"/>
mappers>
package com.SolitudeAlma.dao;
import com.SolitudeAlma.pojo.Blog;
/**
* @author SolitudeAlma
* @date 2021/8/19
*/
public interface BlogMapper {
/**
* 添加一篇文章
* @param blog 文章类
* @return SQL执行结果,影响了几行
*/
int addArticle(Blog blog);
}
sql配置文件
<insert id="addArticle" parameterType="Blog">
insert into blog(`id`, `title`, `author`, `create_time`, `views`)
values (#{id}, #{title}, #{author}, #{createTime}, #{views});
insert>
测试
@Test
public void addArticleTest() throws ParseException {
String strDateFormat = "yyyy-MM-dd HH:mm:ss";
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
SimpleDateFormat dateFormat = new SimpleDateFormat(strDateFormat);
Blog blog = new Blog();
blog.setId(IDUtils.getId());
blog.setTitle("Mybatis如此简单");
blog.setAuthor("SolitudeAlma");
blog.setCreateTime(dateFormat.format(new Date()));
blog.setViews(23333);
int result = mapper.addArticle(blog);
System.out.println(result);
blog.setId(IDUtils.getId());
blog.setTitle("Java如此简单");
blog.setCreateTime(dateFormat.format(new Date()));
result = mapper.addArticle(blog);
System.out.println(result);
blog.setId(IDUtils.getId());
blog.setTitle("微服务如此简单");
blog.setCreateTime(dateFormat.format(new Date()));
result = mapper.addArticle(blog);
System.out.println(result);
blog.setId(IDUtils.getId());
blog.setTitle("Spring如此简单");
blog.setCreateTime(dateFormat.format(new Date()));
result = mapper.addArticle(blog);
System.out.println(result);
sqlSession.close();
}
初始化数据完毕!
需求:根据作者名字和博客名字来查询博客!如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询
/**
* 查询文章信息,并根据if语句进一步查询
* @param map 查询条件集合
* @return List 文章信息集合
*/
List<Blog> queryArticleIf(Map<String, Object> map);
<select id="queryArticleIf" parameterType="map" resultType="Blog">
select * from blog where
<if test="title != null">
title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
select>
@Test
public void queryArticleIfTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String, Object>map = new HashMap<>();
//map.put("title", "Mybatis如此简单");
map.put("author", "SolitudeAlma");
List<Blog> blogs = mapper.queryArticleIf(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
这样写我们可以看到,如果 author 等于 null,那么查询语句为 select * from user where title=#{title},但是如果title为空呢?那么查询语句为 select * from user where and author=#{author},这是错误的 SQL 语句,如何解决呢?请看下面的 where 语句!
修改上面的SQL语句:
<select id="queryArticleIf" parameterType="map" resultType="Blog">
select * from blog
<where>
<if test="title != null">
title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
where>
select>
这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。
有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句
/**
* 查询文章信息,并根据choose语句进一步查询
* @param map 查询条件集合
* @return List 文章信息集合
*/
List<Blog> queryBlogChoose(Map<String, Object> map);
<select id="queryBlogChoose" parameterType="map" resultType="Blog">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title}
when>
<when test="author != null">
and author = #{author}
when>
<otherwise>
and views = #{views}
otherwise>
choose>
where>
select>
@Test
public void queryBlogChooseTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String, Object>map = new HashMap<>();
map.put("views", "23333");
List<Blog> blogs = mapper.queryBlogChoose(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
同理,上面的对于查询 SQL 语句包含 where 关键字,如果在进行更新操作的时候,含有 set 关键词,我们怎么处理呢?
SET元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
/**
* 修改文章信息,并根据trim语句动态修改
* @param map 查询条件集合
* @return SQL执行结果,影响了几行
*/
int updateArticle(Map<String, Object> map);
<update id="updateArticle" parameterType="map" >
update blog
<set>
<if test="title != null">
title=#{title},
if>
<if test="author != null">
author=#{author}
if>
set>
where id =#{id}
update>
@Test
public void updateArticleTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String, Object>map = new HashMap<>();
map.put("id", "de27dc043af84946b008ff6ce3b866d1");
map.put("title", "Mybatis如此简单01");
int i = mapper.updateArticle(map);
System.out.println(i);
sqlSession.close();
}
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
trim>
prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},if>
<if test="password != null">password=#{password},if>
<if test="email != null">email=#{email},if>
<if test="bio != null">bio=#{bio}if>
set>
where id=#{id}
update>
这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
来看看与 set 元素等价的自定义 trim 元素吧:
<trim prefix="SET" suffixOverrides=",">
...
trim>
注意,我们覆盖了后缀值设置,并且自定义了前缀值。
这部分相当于扩展功能,需要的自己实践,所以就直接搬文档了
有时候可能某个 sql 语句我们用的特别多,为了增加代码的复用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
提取SQL片段:
<sql id="if-title-author">
<if test="title != null">
title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
sql>
引用SQL片段:
<select id="queryArticleIf" parameterType="map" resultType="Blog">
select * from blog
<where>
<include refid="if-title-author"/>
where>
select>
注意:
①、最好基于单表来定义 sql 片段,提高片段的可复用性
②、在 sql 片段中不要包括 where
将数据库中前三个数据的id修改为1,2,3;
需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息
/**
* 查询文章信息,根据foreach语句动态查询
* @param map 查询条件集合
* @return List 文章信息集合
*/
List<Blog> queryBlogForeach(Map<String, Object> map);
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
foreach>
where>
select>
@Test
public void testQueryBlogForeachTest() {
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
Map<String, Object> map = new HashMap<>();
List<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(3);
map.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForeach(map);
System.out.println(blogs);
session.close();
}
小结:其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。多在实践中使用才是熟练掌握它的技巧。
1、什么是缓存 [ Cache ]?
2、为什么使用缓存?
3、什么样的数据能使用缓存?
一级缓存也叫本地缓存:
测试:
/**
* 根据id查询用户信息
* @param id 用户唯一标识
* @return User 用户信息类
*/
User getUserById(@Param("id") int id);
<select id="getUserById" resultType="User">
select * from user where id = #{id};
select>
@Test
public void getUserByIdTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
System.out.println("=========================");
User user1 = mapper.getUserById(1);
System.out.println(user1);
System.out.println(user == user1);
sqlSession.close();
}
一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;
一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!
@Test
public void getUserByIdTest01(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession1 = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession1.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
User user2 = mapper2.getUserById(1);
System.out.println(user2);
System.out.println(user==user2);
sqlSession.close();
sqlSession1.close();
}
观察结果:发现发送了两条SQL语句!
结论:每个sqlSession中的缓存相互独立
@Test
public void getUserByIdTest02(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
UserMapper mapper1 = session.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
User user2 = mapper1.getUserById(2);
System.out.println(user2);
System.out.println(user==user2);
session.close();
}
观察结果:发现发送了两条SQL语句!很正常的理解
结论:当前缓存中,不存在这个数据
/**
* 修改用户信息
* @param map 条件以及修改信息的集合
* @return SQL执行的结果,影响了几行
*/
int updateUser(Map<String, Object> map);
编写SQL
<update id="updateUser" parameterType="map">
update user set name = #{name}, pwd = #{pwd} where id = #{id}
</update>
测试
@Test
public void updateUserTest() {
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
Map<String, Object> map = new HashMap<>();
map.put("name","SolitudeAlma");
map.put("id",4);
mapper.updateUser(map);
User user2 = mapper.getUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session.close();
}
观察结果:查询在中间执行了增删改操作后,重新执行了
结论:因为增删改操作可能会对当前数据产生影响
@Test
public void getUserById03(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
session.clearCache();//手动清除缓存
User user2 = mapper.getUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session.close();
}
一级缓存就是一个map
<setting name="cacheEnabled" value="true"/>
<cache/> 直接使用这个需要序列化类(org.apache.ibatis.cache.CacheException: Error serializing object. Cause: java.io.NotSerializableException: com.SolitudeAlma.pojo.User),实现Serializable接口
或
官方示例=====>查看官方文档
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
SqlSession session2 = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
session.close();
User user2 = mapper2.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session2.close();
}
第三方缓存实现–EhCache: 查看百度百科
Ehcache是一种广泛使用的java分布式缓存,用于通用缓存;
要在应用程序中使用Ehcache,需要引入依赖的jar包
<dependency>
<groupId>org.mybatis.cachesgroupId>
<artifactId>mybatis-ehcacheartifactId>
<version>1.2.1version>
dependency>
在mapper.xml中使用对应的缓存即可
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
编写ehcache.xml文件,如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置。
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
ehcache>
运行后会爆红
https://blog.csdn.net/qq_42262803/article/details/86485140
https://blog.csdn.net/theonegis/article/details/45873331
https://blog.csdn.net/langtian08/article/details/82015017
合理的使用缓存,可以让我们程序的性能大大提升!
附属代码
所有资源来源于狂神说的B站投稿视频