MyBatis
提示:这里可以添加本文要记录的大概内容:
一、MyBatis是什么?
持久层(Dao层)框架,优点
1、简单易学灵活
2、sql和代码的分离,提高了可维护性。
3、提供映射标签,支持对象与数据库的orm字段关系映射提供对象关系映射标签,支持对象关系组建维护
4、提供xml标签,支持编写动态sql
二、使用步骤
新建Mavean项目
1.创建数据库文件
CREATE DATABASE `mybatis` ;
use mybatis;
CREATE TABLE `user` (
`id` int NOT NULL,
`name` varchar(30) DEFAULT NULL,
`pwd` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
2.导入依赖
pom.xml文件中
<dependencies>
<!--mybatis依赖-->
<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.21</version>
</dependency>
<!--junit依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<!--解决资源无法生效或无法导出-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>**/*.properties</exclude>
<exclude>**/*.xml</exclude>
</excludes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
3.编写mybatis核心配置文件
在resource文件夹下新建mybatis-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?useSSL=false&
useUnicode=true&serverTimezone=GMT%2B8&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--为每一个Mapper配置文件注册-->
<mappers>
<mapper resource="com/veterlemon/dao/UserMapper.xml"/>
</mappers>
</configuration>
4.编写mybatis工具类
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory = null;
static {
try {
// 获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
5.编写代码
1、建立pojo的实体类
public class User {
private int id;
private String name;
private String pwd;
public User() { }
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 + '\'' +
'}';
}
}
2、在Dao层创建对应Mapper类
(实际上是讲原本的接口类(Dao类)转变成Mapper类)
public interface UserMapper {
//查询所有用户
List<User> getUserList();
}
3、为Dao类创建Mapper.xml文件,并进行配置
(实际上是将原本的接口实现类(DapImpl)转变成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">
<!--namespace:绑定一个Dao/Mapper接口-->
<mapper namespace="com.veterlemon.dao.UserMapper">
<!--select查询语句;id对应Dao内的方法名;resultType/resultMap表示sql语句执行后返回的一个/多个结果集-->
<select id="getUserList" resultType="com.veterlemon.pojo.User">
<!--sql语句-->
select * from mybatis.user
</select>
</mapper>
6.测试
使用junit进行测试,建议test下的java包与main下的java包保持一致
public class UserDaoTest {
@Test
public void test(){
// 1.获取sqlSession对象
SqlSession sqlSession = MyBatisUtils.getSqlSession();
try {
// 2.执行sql
// 方式一:selectXX()
//List<User> userList = sqlSession.selectList("com.veterlemon.dao.UserDao.getUserList");
// 方式二:getMapper(),推荐使用
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
//遍历
for (User user : userList) {
System.out.println(user);
}
} catch (Exception e){
e.printStackTrace();
} finally {
//关闭sqlSession
sqlSession.close();
}
}
}
三、MyBatis的CRUD
在mybatis环境下添加查询方法,步骤:
先在Mapper类中新增方法,再去对应的Mapper.xml文件中绑定新增方法并编写sql
1.操作Dao层的UserMapper类
在Dao包下的UserMapper类中添加新的查询方法
public interface UserMapper {
//查询所有用户
List<User> getUserList();
//查询所有用户(根据id)
User getUserById(int id);
//新增用户
int insertUser(User user);
//修改用户
int updateUser(User user);
//删除用户
int deleteUser(int id);
//模糊查询(根据名字)
List<User> getUserLike(String name);
}
2.操作对应Dao层的Mapper.xml文件
<!--parameterType="com.veterlemon.pojo.User",可以直接取到类中对象的属性-->
<?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.veterlemon.dao.UserMapper">
<!--查询所有用户-->
<select id="getUserList" resultType="com.veterlemon.pojo.User">
select * from mybatis.user
</select>
<!--条件查询(根据用户id)-->
<select id="getUserById" parameterType="int" resultType="com.veterlemon.pojo.User">
select * from mybatis.user where id = #{id};
</select>
<!--添加用户-->
<insert id="insertUser" parameterType="com.veterlemon.pojo.User">
insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd});
</insert>
<!--更新用户信息-->
<update id="updateUser" parameterType="com.veterlemon.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}
</update>
<!--删除用户-->
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id=#{id}
</delete>
<!--根据名字进行模糊查询-->
<select id="getUserLike" resultType="com.veterlemon.pojo.User">
select * from mybatis.user where name like "%"#{name}"%";
</select>
</mapper>
3.进行测试
增删改操作需要提交事务
public class UserDaoTest {
//测试:查询所有用户
@Test
public void getUserList_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
try {
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
List<User> userList = userDao.getUserList();
//遍历
for (User user : userList) {
System.out.println(user);
}
} catch (Exception e){
e.printStackTrace();
} finally {
//关闭sqlSession
sqlSession.close();
}
}
//测试:查询所有用户(根据id)
@Test
public void getUserById_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
//测试:新增用户
@Test
public void insertUser_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.insertUser(new User(4, "张三", "1555"));
if (result > 0){
System.out.println("用户添加成功");
}else {
System.out.println("用户添加失败");
}
//提交事务
sqlSession.commit();
sqlSession.close();
}
//测试:更新用户信息
@Test
public void updateUser_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.updateUser(new User(4, "李白", "123"));
if (result > 0){
System.out.println("用户修改成功");
}else {
System.out.println("用户修改失败");
}
//提交事务
sqlSession.commit();
sqlSession.close();
}
//测试:删除用户
@Test
public void deleteUser_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.deleteUser(4);
if (result > 0){
System.out.println("用户删除成功");
}else {
System.out.println("用户删除失败");
}
//提交事务
sqlSession.commit();
sqlSession.close();
}
//模糊查询(根据名字)
@Test
public void getUserLike(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserLike("李");
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
}
4.用Map改良Mapper(注解更佳)
当实体类或数据库的表中参数或字段过多时,应该考虑用Map来优化,以便减少不必要的操作
优化Mapper
//UserMapper类中
//优化增删改操作,以新增操作为例子
int insertUserMap(Map<String, Object> map);
//UserMapper.xml文件中
<!--属于优化Map操作的语句,传递过来的是map的key,所以可以自定义-->
<insert id="insertUserMap" parameterType="map">
insert into mybatis.user (id, name, pwd) values (#{user_id},#{username},#{password});
</insert>
//测试类中
//测试:优化添加用户测试
@Test
public void insertUserMap_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<>();
//这里的键值对要与UserMapper.xml中的sql属性对应
map.put("user_id", 6);
map.put("username", "李逵");
map.put("password", "321");
int result = mapper.insertUserMap(map);
if (result > 0){
System.out.println("Map优化之用户添加成功");
}else {
System.out.println("Map优化之用户添加失败");
}
//提交事务
sqlSession.commit();
sqlSession.close();
}
四、MyBatis的配置解析
mybatis-config.xml内的
1.环境、事务管理器、数据源
<environments default="development">
<!--可以有多个environment,由最外层的default选中-->
<environment id="development">
<!--事务管理器,有JDBC和MANAGED两种-->
<transactionManager type="JDBC"/>
<!--数据源,有unpooled、pooled、judi三种-->
<dataSource type="POOLED">
<!--属性,通过property的属性实现引用配置文件-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&serverTimezone=GMT%2B8&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
2.属性
properties,作用:引入外部配置文件
//db.properties文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&serverTimezone=GMT%2B8&characterEncoding=UTF-8
username=root
password=123456
//mybatis-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>
<!--引入外部配置文件-->
<properties resource="db.properties">
<!--也可以将dp.properties的属性写到这-->
<!--使用顺序:先使用properties内部的,然后去读外部文件,若外部文件有重复的属性,则覆盖内部的-->
<property name="password" value="1111"/>
</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>
<!--为每一个Mapper配置文件注册-->
<mappers>
<mapper resource="com/veterlemon/dao/UserMapper.xml"/>
</mappers>
</configuration>
3.类型别名
typeAliases,减少类名完全限定名的冗余
(当实体类多时,建议使用形式二或注解)
//mybatis-config.xml文件中
<!--类型别名:给实体类起别名-->
<typeAliases>
<!--形式一:typeAlias,设置类名别名-->
<typeAlias type="com.veterlemon.pojo.User" alias="User"/>
<!--形式二:package,搜索包名,将【其下的实体类】别名设置为【对应的首字母小写的类名】-->
<package name="com.veterlemon.pojo"/>
</typeAliases>
//对应的UserMapper.xml文件中
<!--使用别名形式一-->
<select id="getUserList" resultType="User">
select * from mybatis.user
</select>
<!--使用别名形式二-->
<select id="getUserById" parameterType="int" resultType="user">
select * from mybatis.user where id = #{id};
</select>
4.★★设置★★
settings,非常重要
mapUnderscoreToCamelCase,是否开启自动驼峰命名规则映射
loglmpl,指定MyBatis所用日志的具体实现,未指定时将自动查找
5.映射器
mappers,作用:为每个Mapper类绑定注册
<mappers>
<!--方式一:resource,推荐-->
<mapper resource="com/veterlemon/dao/UserMapper.xml"/>
<!--方式二:通过类名完全限定,接口和对应的Mapper配置文件必须同名且在同个包下-->
<mapper class="com.veterlemon.dao.UserMapper"/>
<!--方式三:使用包扫描,接口和对应的Mapper配置文件必须同名且在同个包下-->
<package name="com.veterlemon.dao"/>
</mappers>
五、生命周期和作用域
错误的使用会导致严重的【并发问题】
1.SqlSessionFactoryBuilder
一旦创建了SqlSessionFactory就不需要SqlSessionFactoryBuilder了,故应设为局部变量
2.SqlSessionFactory
可以理解为数据库连接池,一旦创建就一直存在,不需要重复创建,应该设为全局变量
3.SqlSession
一个连接数据库连接池的请求,线程不安全所以用完之后必须要关闭
六、解决属性名与字段名不一致问题
当数据库【表中的字段】与【实体类中定义的变量名】不一致时,可能会导致该不一致的属性无法被获取,如下:
表字段
实体类
解决1:起别名
通过类型处理器,在对应的Mapper.xml中修改sql
<select id="getUserById" resultType="User">
select id,name,pwd from mybatis.user where id= #{id}
</select>
解决2:使用resultMap
简单的使用,当表存在一对多、多对多等关系时,需要使用resultMap的高级技巧
<!--结果集映射-->
<resultMap id="UserMap" type="User">
<!--column:数据库中的字段,property实体类中的属性,只需要映射不一致的即可-->
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserById" resultMap="UserMap">
select * from mybatis.user where id= #{id}
</select>
七、日志
1.LOG4J
在mybatis-config.xml中开启日志
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
在pom.xml中导包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
再resource包下新建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/veterlemon.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
简单使用
public class UserDaoTest {
static Logger logger = Logger.getLogger(UserDaoTest.class);
//测试:查询所有用户
@Test
public void getUserList_Test() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
logger.info("测试,成功进入getUserList_Test()方法");
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
User user = userDao.getUserById(1);
System.out.println(user);
sqlSession.close();
}
@Test
public void Log4j_Test(){
logger.info("info:进入了log4j_test");
logger.debug("debug:进入了log4j_test");
logger.error("error:进入了log4j_test");
}
}
2.STDOUT_LOGGING
在mybatis-config.xml中开启日志
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
八、分页
1.limit分页
编写接口类(Mapper)
public interface UserMapper {
//limit分页
List<User> getUserByLimit(Map<String, Integer> map);
}
在相应Mapper.xml文件添加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.veterlemon.dao.UserMapper">
<!--设置map的结果集映射,解决表字段与实体类属性不一致问题-->
<resultMap id="UserMap" type="User">
<result column="pwd" property="password"/>
</resultMap>
<!--分页-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>
</mapper>
测试
public class UserDaoTest {
//测试:limit分页
@Test
public void getUserByLimit_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Integer> map = new HashMap<>();
map.put("startIndex", 0);
map.put("pageSize", 3);
List<User> userList = mapper.getUserByLimit(map);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
}
2.rowBounds分页
底层实际上是limit
编写接口类(Mapper)
public interface UserMapper {
//rowBounds分页
List<User> getUserByRowBounds();
}
在相应Mapper.xml文件添加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.veterlemon.dao.UserMapper">
<!--设置map的结果集映射,解决表字段与实体类属性不一致问题-->
<resultMap id="UserMap" type="User">
<result column="pwd" property="password"/>
</resultMap>
<!--rowBounds分页-->
<select id="getUserByRowBounds" resultMap="UserMap">
select * from mybatis.user
</select>
</mapper>
测试
public class UserDaoTest {
//测试:rowBounds分页
@Test
public void getUserByRowBounds_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
new RowBounds(0, 3);
//通过java层面实现分页
List<User> userList = sqlSession.selectList("com.veterlemon.dao.UserMapper.getUserByRowBounds");
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
}
3.分页插件
PageHelper插件
底层实际上是limit
九、使用注解开发
本质:反射机制
底层:动态代理
在实际开发中,实际上是面向接口编程,使用注解进行开发更符合开发过程
(根本原因:解耦合、提高复用率、可拓展、分层开发)
1.简单使用
复杂的sql语句并不适合用注解进行控制
注意@Param注解
编写Mapper类
public interface UserMapper {
//注解方式,获取所有用户
@Select("select * from mybatis.user")
List<User> getUsers();
}
★★在mybatis-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>
<!--引入配置文件-->
<properties resource="db.properties"/>
<!--设置-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--为实体类起别名-->
<typeAliases>
<typeAlias type="com.veterlemon.pojo.User" alias="User"/>
</typeAliases>
<!--环境-->
<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 class="com.veterlemon.dao.UserMapper"/>
</mappers>
</configuration>
测试
public class UserDaoTest {
//测试:获取所有用户
@Test
public void getUsers_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUsers();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
}
2.使用注解进行CRUD
在工具类创建的时候自动提交事务(不推荐)
(在openSession中设置为true即可实现事务的自动提交)
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory = null;
static {
try {
// 获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//在openSession中设置为true即可实现事务的自动提交
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
}
编写Mapper类,添加注解
public interface UserMapper {
//注解方式,获取所有用户
@Select("select * from mybatis.user")
List<User> getUsers();
//注解方式,根据id获取用户
//方法存在多个参数时,使用多个@Param即可(#{uid}的值取@Param中的uid))
@Select("select * from mybatis.user where id=#{uid}")
User getUserById(@Param("uid")int id);
//注解方式,添加用户
@Insert("insert into mybatis.user(id, name, pwd) values (#{id}, #{name}, #{password})")
int addUser(User user);
//注解方式,修改用户信息
@Update("update mybatis.user set name=#{name},pwd=#{password} where id=#{id}")
int updateUser(User user);
//注解方式,删除用户
@Delete("delete from mybatis.user where id=#{uid}")
int deleteUser(@Param("uid") int id);
}
进行测试
public class UserDaoTest {
//测试:获取所有用户
@Test
public void getUsers_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUsers();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
//测试:根据id获取用户
@Test
public void getUserById_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
//测试:添加用户
@Test
public void addUser_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.addUser(new User(7, "小陆", "520"));
if (result > 0)
System.out.println("用户添加成功");
sqlSession.close();
}
//测试:修改用户信息
@Test
public void updateUser_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.updateUser(new User(7, "小梁", "222"));
if (result > 0)
System.out.println("用户信息修改成功");
sqlSession.close();
}
//测试:删除用户
@Test
public void deleteUser_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.deleteUser(7);
if (result > 0)
System.out.println("删除用户成功");
sqlSession.close();
}
}
10、Lombok
一个插件,作用:使用注解去替代实体类中的get/set等方法。使用步骤如下
(不推荐使用,因为无法自动生成【多参数构造器】的【重构】需要手动添加,会养成坏习惯)
1.安装Lombok插件
Idea中的settings->Plugins->搜索Lombok->选中使用人数最多的那款(2020款及以上Idea自带)
2.添加依赖
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
3.为实体类添加注解
/*
@Data:无参构造、get/set、toString、hashcode、equals
@AllArgsConstructor:全参构造,一旦添加@AllArgsConstructor,则需要再添加@NoArgsConstructor
@NoArgsConstructor:无参构造
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode
public class User {
private int id;
private String name;
private String password;
}
十一、★★MyBatis涉及表的映射关系之一对多★★
同MySQL中的一对多关系,见:https://editor.csdn.net/md/?articleId=123124202
一个老师对应多个学生
1.环境搭建
新建数据库表
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, '秦老师');
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=utf8
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');
在pom.xml文件中导入Lambok
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
在pojo包下新建实体类并添加注解
//Student类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
//学生需要关联一个老师
private Teacher teacher;
}
//Teacher类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
}
在dao包下建立Mapper接口
//StudentMapper类
public interface StudentMapper {
//查询所有学生信息以及其对应的老师信息
public List<Student> getStudent();
}
//TeacherMapper类
public interface TeacherMapper {
//查询所有老师
@Select("select * from mybatis.teacher")
List<Teacher> getTeacher();
//按id查询老师
@Select("select * from mybatis.teacher where id=#{tid}")
Teacher getTeacherById(@Param("tid") int id);
}
在resource文件夹下建立Mapper.xml文件
StudentMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.veterlemon.dao.StudentMapper">
</mapper>
TeacherMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.veterlemon.dao.TeacherMapper">
</mapper>
在mybatis-config.xml绑定注册
<!--绑定接口-->
<mappers>
<mapper class="com.veterlemon.dao.TeacherMapper"/>
<mapper class="com.veterlemon.dao.StudentMapper"/>
</mappers>
测试
//测试:查询所有学生信息以及其对应的老师信息
@Test
public void getStudent_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.getStudent();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
//测试:按id获取老师
@Test
public void getTeacherById_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacherById(1);
System.out.println(teacher);
sqlSession.close();
}
2.解决数据库表字段与实体类属性不一致问题(实现多表查询)
在StudentMappere.xml文件中写入
按嵌套查询(子查询)
<!--思路:1.查出所有的学生信息,2.根据学生的tid查找对应老师-->
<select id="getStudent" resultMap="StudentTeacher">
select * from mybatis.student
</select>
<resultMap id="StudentTeacher" type="Student">
<result column="id" property="id"/>
<result column="name" property="name"/>
<!--处理复杂的属性,是对象则使用association,是集合则使用collection-->
<!--javaType:为association的复杂类型申明类型(即解释该复杂类型),select嵌套查询指向操作-->
<association column="tid" property="teacher" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from mybatis.teacher where id=#{id}
</select>
★★按结果查询(联表查询)★★
resultMap实际上相当于在解析实体类中的属性,将其与数据库的表字段相映射
根据实体类的复杂属性选对象association或集合collection
<select id="getStudent" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname
from mybatis.student s,mybatis.teacher t
where s.tid = t.id;
</select>
<resultMap id="StudentTeacher2" type="Student">
<result column="sid" property="id"/>
<result column="sname" property="name"/>
<!--处理Student实体类中的teacher属性-->
<association property="teacher" javaType="Teacher">
<result column="tname" property="name"/>
</association>
</resultMap>
十二、★★MyBatis涉及表的映射关系之多对一★★
多个学生有同一个老师
1.环境搭建
新建数据库表
在pom.xml文件中导入Lambok
在pojo包下新建实体类并添加注解
//学生类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
//学生只有一个老师
private int tid;
}
//老师类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
//老师拥有多个学生
private List<Student> students;
}
在dao包下建立Mapper接口
//TeacherMapper
public interface TeacherMapper {
//查询所有老师
List<Teacher> getTeacher();
}
//StudentMapper
public interface StudentMapper {}
在resource文件夹下建立Mapper.xml文件
<!--TeacherMapper.xml-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.veterlemon.dao.TeacherMapper">
<select id="getTeacher" resultType="Teacher">
select * from mybatis.teacher
</select>
</mapper>
<!--StudentMapper.xml-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.veterlemon.dao.StudentMapper"></mapper>
测试
public class UserDaoTest {
//测试:查询所有老师信息
@Test
public void getStudent_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> teacherList = mapper.getTeacher();
for (Teacher teacher : teacherList) {
System.out.println(teacher);
}
sqlSession.close();
}
}
2.解决数据库表字段与实体类属性不一致问题(实现多表查询)
★★按结果查询★★
resultMap实际上相当于在解析实体类中的属性,将其与数据库的表字段相映射
根据实体类的复杂属性选对象association或集合collection
使用association时用javaType,使用collection时使用ofType
在TeacherMapper.xml中写入
<!--查询所有老师-->
<select id="getTeacher" resultType="Teacher">
select * from mybatis.teacher
</select>
<!--根据id获取老师,并获取该老师下的所有学生-->
<select id="getTeacherById" resultMap="TeacherStudent">
select t.id tid, t.name tname, s.id sid, s.name sname
from mybatis.student s, mybatis.teacher t
where s.tid=t.id and t.id=#{tid}
</select>
<!--实现结果集映射:按结果查询-->
<resultMap id="TeacherStudent" type="Teacher">
<result column="tid" property="id"/>
<result column="tname" property="name"/>
<!--解决实体类中的List<Student>,collection中用ofType(association使用javaType)-->
<collection property="students" ofType="Student">
<result column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="tid" property="tid"/>
</collection>
</resultMap>
3.一对多与多对一的总结
sql语句可以使用【子查询】的方法,也可以使用【结果查询】的方法(推荐)
多对一:多对一的sql语句采用关联association(用javaType)
一对多:一对多的sql语句采用集合collection(用ofType)
十四、动态SQL
根据不同条件生成不同SQL语句
实际上与jstl标签库一致
1.环境搭建
创建数据库表
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;
添加依赖
编写配置文件
<?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"/>
<!--设置-->
<settings>
<!--设置日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--启动数据库表与实体类属性的驼峰命名映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--为实体类起别名-->
<typeAliases>
<package name="com.veterlemon.pojo"/>
</typeAliases>
<!--环境-->
<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 class="com.veterlemon.dao.BlogMapper"/>
</mappers>
</configuration>
编写实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
添加新工具类
解决id操作后出现间断的问题
public class IdUtils {
//解决int型id在进行CRUD操作后无法连续的问题
public static String getId(){
return UUID.randomUUID().toString().replaceAll("-", "");
}
}
编写实体类对应的Mapper接口和Mapper.xml文件
//Mapper接口
public interface BlogMapper {
//插入数据
int addBlog(Blog blog);
}
//对应的Mapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.veterlemon.dao.BlogMapper">
<insert id="addBlog">
insert into mybatis.blog (id, title, author, create_time, views)
values (#{id}, #{title}, #{author}, #{createTime}, #{views})
</insert>
</mapper>
测试
public class BlogDaoTest {
//测试:添加blog
@Test
public void addBlog_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IdUtils.getId());
blog.setTitle("Mybatis");
blog.setAuthor("张三");
blog.setCreateTime(new Date());
blog.setViews(9999);
mapper.addBlog(blog);
blog.setId(IdUtils.getId());
blog.setTitle("Java");
mapper.addBlog(blog);
blog.setId(IdUtils.getId());
blog.setTitle("Spring");
mapper.addBlog(blog);
blog.setId(IdUtils.getId());
blog.setTitle("微服务");
mapper.addBlog(blog);
sqlSession.close();
}
}
2.动态SQL之if标签
BlogMapper.xml文件添加
<!--查询Blog,设置1=1是为了让where后面的if都不成立时仍然可以执行最外层的SQL,不推荐使用-->
<select id="getBlogIf" parameterType="map" resultType="Blog">
select * from mybatis.blog where 1=1
<if test="title != null">
<!--在外层SQL上拼接-->
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</select>
测试
//测试:查询blog
@Test
public void getBlog_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap<>();
//设置第一个条件
map.put("title", "Java");
//设置第二个条件
map.put("author", "张三");
List<Blog> blogIf = mapper.getBlogIf(map);
for (Blog blog : blogIf) {
System.out.println(blog);
}
sqlSession.close();
}
存在的问题:在SQL语句中添加1=1并不合理,去掉1=1后
<!--查询Blog,设置1=1是为了让where后面的if都不成立时仍然可以执行最外层的SQL,不推荐使用-->
<select id="getBlogIf" parameterType="map" resultType="Blog">
select * from mybatis.blog where
<if test="title != null">
<!--在外层SQL上拼接-->
title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</select>
情况1:若满足第一层条件或第一第二层条件则可以顺利执行(即select * from mybatis.blog where title=#{title}或select * from mybatis.blog where title=#{title} and author=#{author})
//测试:查询blog
@Test
public void getBlog_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap<>();
//设置第一个条件
map.put("title", "Java");
//设置第二个条件
map.put("author", "张三");
List<Blog> blogIf = mapper.getBlogIf(map);
for (Blog blog : blogIf) {
System.out.println(blog);
}
sqlSession.close();
}
可成功执行
情况2:不满足第一层条件但是满足第二层条件,则会执行错误,因为是SQL语句的拼接出了问题(即select * from mybatis.blog where and author=#{author})
//测试:查询blog
@Test
public void getBlog_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap<>();
//设置第一个条件
//map.put("title", "Java");
//设置第二个条件
map.put("author", "张三");
List<Blog> blogIf = mapper.getBlogIf(map);
for (Blog blog : blogIf) {
System.out.println(blog);
}
sqlSession.close();
}
执行错误
3.动态SQL之trim(where、set)标签
针对纯 if 标签出现的问题,可使用where标签解决(where可以自动省略或添加and)
<select id="getBlogChoose" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<if test="title != null">
title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</where>
</select>
<!--更新blog-->
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author},
</if>
<if test="views != null">
views = #{views}
</if>
</set>
where id=#{id}
</update>
4.动态SQL之choose(when、otherwise)标签
相当于switch
<select id="getBlogChoose" parameterType="map" resultType="Blog">
select * from mybatis.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>
//查询Blog之动态SQL->choose
@Test
public void getBlogChoose_Test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap<>();
//设置第一个条件
map.put("title", "Java");
//设置第二个条件
map.put("author", "狂神说");
//设置第三个条件
map.put("views", "9999");
List<Blog> blogIf = mapper.getBlogChoose(map);
for (Blog blog : blogIf) {
System.out.println(blog);
}
sqlSession.close();
}
5.动态SQL之SQL片段
抽取SQL语句,实现复用
由于where标签的不确定性,故推荐基于单表使用SQL片段(SQL片段内最好不要包括where标签)
<sql id="if-title-author">
<if test="title != null">
title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</sql>
<select id="getBlogChoose" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<include refid="if-title-author"/>
</where>
</select>
<!--相当于-->
<select id="getBlogChoose" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<if test="title != null">
title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</where>
</select>
6.## 5.动态SQL之foreach标签
代码解析:
允许指定一个集合collection,集合内每个遍历出来的元素由item命名,可以根据index取值,open定义sql开头的形式,close定义sql关闭的形式,separator定义每个元素之间的分隔符
通常用于查询指定范围的记录
<!--查询id为1-3的blog记录-->
<select id="selectBlogIn" resultType="Blog">
select * from mybatis.blog b
<where>
<foreach item="id" collection="idList" open="and (" separator="or" close=")">
id = #{id}
</foreach>
</where>
</select>
十五、缓存
1.缓存简介
缓存:存放在内存中的临时数据,减少和数据库的交互次数和系统开销
目的:为了高并发、高可用、高性能
一级缓存:sqlSession级别的缓存(自动开启)
二级缓存:基于namespace级别的缓存(需要手动开启)
2.一级缓存(会话级缓存)
也叫本地缓存,只在一次会话中有效(连接到关闭),代表:SqlSession
一级缓存相当于一个Map
缓存失效的情况:1.进行增删改操作后,会自动刷新缓存;2.查询不同对象;3.手动清除缓存
手动清除缓存:clearCache()
3.二级缓存(接口级缓存)
开启二级缓存后,一级缓存若关闭,则其数据会保存到二级缓存中,新的会话可以从二级缓存中获得数据相当于数据的转存
在mybatis-config.xml的<settings>
中
<settings>
<!--设置日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--启动数据库表与实体类属性的驼峰命名映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
在待使用的Mapper.xml文件中添加<cache/>
<cache
eviction="FIFO" <!--缓存的清除策略-->
flushInterval="60000" <!--缓存的刷新时间-->
size="512" <!--可存储的对象或列表大小-->
readOnly="true" <!--返回的对象是否只读-->
/>
测试
要么添加上面形式的<cache/>,要么就在实体类处实现序列化(implements Serializable),否则测试会报错
@Test
public void test(){
Sqlsession sqlSession = Mybatisutils.getsqLSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
system.out.println(user) ;
sqlsession.close( );
Sqlsession sqlsession2 = Mybatisutils.getsqlSession();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = mapper2.queryUserById(1);
system.out.print1n(user2);
system.out.println(user==user2);
sqlsession2.close();
}
3.MyBatis缓存顺序
先查看二级缓存,二级缓存没有再查看一级缓存,一级缓存也没有才访问数据库
4.自定义缓存-Ehcache
开源的分布式缓存,主要面向通用缓存,了解即可
现在用Redis数据库做缓存
添加依赖
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
在Mapper.xml文件中
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
resource包下新建ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>