Mybatis笔记一
一、Mybatis介绍
1、什么是Mybatis
官方文档:https://mybatis.org/mybatis-3/zh/getting-started.html
1、MyBatis 是一款优秀的持久层框架
2、它支持定制化 SQL、存储过程以及高级映射。
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
3、MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
4、MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。
5、2013年11月迁移到Github
2、持久化
1、 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
2、 内存:断电即失
3、 数据库(Jdbc),io文件持久化。
为什么需要他持久化
1、 有一些对象,不能让他丢掉。
2、 内存太贵了
3、持久层
Dao层,Service层,Controller层….
完成持久化工作的代码块
层界限十分明显
4、为什么需要Mybatis
- 帮助程序员将数据存入到数据库中
- 方便
- 传统的JDBC代码太复杂了。简化。框架。自动化。
- 不用Mybatis也可以。更容易上手。 技术没有高低之分
- 优点:
1.简单易学
2.灵活
3.sql和代码的分离,提高了可维护性。
4.提供映射标签,支持对象与数据库的orm字段关系映射
5.提供对象关系映射标签,支持对象关系组建维护
6.提供xml标签,支持编写动态sql。
二、第一个Mybatis程序
思路:搭建环境–>导入Mybatis–>编写代码–>测试!
1、搭建环境
搭建数据库
-- 建库
create database `mybatis`;
use `mybatis`
-- 建表
create table `user`(
`id` int(20) not NULL PRIMARY KEY,
`name` VARCHAR(30) DEFAULT null,
`pwd` VARCHAR(30) DEFAULT NULL
) ENGINE=INNODB DEFAULT CHARSET=utf8; -- 指定引擎,指定字符集
-- 插入数据
INSERT into `user`(`id`,`NAME`,`pwd`) VALUES
(1,'张三','123456'),
(2,'张四','123456'),
(3,'张五','123456'),
(4,'张六','123456');
新建maven项目
添加项目依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 父工程-->
<groupId>com.jjl</groupId>
<artifactId>mybatis-study</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 导入依赖-->
<dependencies>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2、创建一个模块
- mybatis从 XML 中构建 SqlSessionFactory
<?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核心配置文件--> <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?userSSL=true&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="1234qwer"/> </dataSource> </environment> </environments> </configuration>
- 编码mybatis工具类
package com.jjl.utils; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import static org.apache.ibatis.io.Resources.*; //sqlSessionFactory 工具类 public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { //获取sqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } // 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。 // 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。 public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
3、编写代码
-
实体类
package com.jjl.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 + '\'' + '}'; } }
-
dao接口
package com.jjl.dao; import com.jjl.pojo.User; import java.util.List; public interface UserDao { List<User> getUserList(); }
-
接口实现类,由原来的UserDaoImpl转变为一个Mapper配置文件
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace绑定一个对应的dao/mapper接口-->
<mapper namespace="com.jjl.dao.UserDao">
<!-- 查询语句-->
<select id="getUserList" resultType="com.jjl.pojo.User">
select * from user;
</select>
</mapper>
4、测试
- 注意报错
问题1:
org.apache.ibatis.binding.BindingException: Type interface com.jjl.dao.UserDao is not known to the MapperRegistry.
解决:
在pom.xml中添加
<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>
问题2:
Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 1 字节的 UTF-8 序列的字节 1 无效
解决:
测试
package com.jjl.dao;
import com.jjl.pojo.User;
import com.jjl.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserDaoTest {
@Test
public void test(){
//获得sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//执行sql:方法一
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
//执行sql:方法二
//List<User> userList = sqlSession.selectList("com.jjl.dao.UserDao.getUserList");
for (User user : userList) {
System.out.println(user.toString());
}
//关闭sqlSession
sqlSession.close();
}
}
三、CRUD(增删改查)
1、namespace
namespace中的包名要和Dao/mapper接口的包一致!
2、select、insert、update、delete
参数
- id:就是对应的namespace中的方法名;
- resultType:sql语句的返回值!
- parameterType:参数类型
3、实现步骤
1、编写接口
package com.jjl.dao;
import com.jjl.pojo.User;
import java.util.List;
public interface UserMapper {
//获取全部用户
List<User> getUserList();
//根据id查询用户
User getUserById(int id);
//insert一个用户
int addUser(User user);
//修改数据
int updateUser(User user);
//删除用户
int deleteUser(int id);
}
2、编写对应的mapper中的sql语句
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace绑定一个对应的dao/mapper接口-->
<mapper namespace="com.jjl.dao.UserMapper">
<!-- 查询语句-->
<select id="getUserList" resultType="com.jjl.pojo.User">
select * from mybatis.user;
</select>
<!-- 更加id查询用户-->
<select id="getUserById" resultType="com.jjl.pojo.User" parameterType="int">
select * from mybatis.user where id = #{id};
</select>
<!-- 插入用户-->
<insert id="addUser" parameterType="com.jjl.pojo.User">
insert into user(id,name,pwd)value (#{id},#{name},#{pwd});
</insert>
<!-- 修改数据-->
<update id="updateUser" parameterType="com.jjl.pojo.User">
update user set name = #{name},pwd = #{pwd} where id = #{id};
</update>
<!-- 删除用户-->
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>
</mapper>
3、测试
增删改需要提交事务:sqlSession.commit();
package com.jjl.dao;
import com.jjl.pojo.User;
import com.jjl.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserDaoTest {
//测试查询所有用户信息
@Test
public void test() {
//获得sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//执行sql:方法一
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
//执行sql:方法二
//List<User> userList = sqlSession.selectList("com.jjl.dao.UserDao.getUserList");
for (User user : userList) {
System.out.println(user);
}
//关闭sqlSession
sqlSession.close();
}
//指定id查询用户信息
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User userById = mapper.getUserById(1);
System.out.println(userById);
sqlSession.close();
}
//添加用户信息
@Test
public void addUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int user = mapper.addUser(new User(5, "shan", "123"));
if (user>0){
sqlSession.commit();
System.out.println("插入成功");
}
sqlSession.close();
}
//更新用户信息
@Test
public void updateUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int updateUser = mapper.updateUser(new User(2, "jjl", "123123"));
if (updateUser>0){
sqlSession.commit();
System.out.println("更新成功");
}
sqlSession.close();
}
//删除用户
@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int deleteUser = mapper.deleteUser(4);
if (deleteUser>0){
sqlSession.commit();
System.out.println("删除成功");
}
sqlSession.close();
}
}
4、map
1、编写接口
// 使用MAP
//insert一个用户
int addUserMap(Map<String,Object> map);
//根据条件查询用户
User getUserByIdMap(Map<String,Object> map);
2、编写对应的mapper中的sql语句
<!-- Map-->
<!-- 添加记录-->
<insert id="addUserMap" parameterType="map">
insert into user(id,name,pwd)value (#{userid},#{username},#{userpwd});
</insert>
<!-- 根据条件查询用户-->
<select id="getUserByIdMap" resultType="com.jjl.pojo.User" parameterType="map">
select * from mybatis.user where id = #{id} and name=#{name};
</select>
3、测试
// Map
@Test
public void addUserMap(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("userid",6);
hashMap.put("username","xl");
hashMap.put("userpwd","1234123");
int userMap = mapper.addUserMap(hashMap);
if (userMap>0){
sqlSession.commit();
System.out.println("插入成功");
}
sqlSession.close();
}
//指定id查询用户信息
@Test
public void getUserByIdMap(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> hashMap = new HashMap<String, Object>();
hashMap.put("id",4);
hashMap.put("name","xl");
User userByIdMap = mapper.getUserByIdMap(hashMap);
System.out.println(userByIdMap);
sqlSession.close();
}
Map传递参数,直接在sql中取出key即可
而对象传递参数,直接在sql中取对象的属性即可
只有一个基本类型参数情况下,可以直接在sql中取到
多个参数用Map,或者注解
5、模糊查询
1、编写接口
List<User> getUserLike(String value);
2、对应接口实现的sql语句
<!-- like模糊查询-->
<select id="getUserLike" resultType="com.jjl.pojo.User">
select * from user where name like "%"#{value}"%";
</select>
3、测试
// like模糊查询
//指定id查询用户信息
@Test
public void getUserLike(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userLike = mapper.getUserLike("张");
for (User user : userLike) {
System.out.println(user);
}
sqlSession.close();
}
四、配置解析
1、核心配置文件
- mybatis-config.xml
- Mybatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息
configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器)
2、环境配置(environments)
MyBatis 可以配置成适应多种环境
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
- 在 MyBatis 中有两种类型的事务管理器(也就是 type=“[JDBC|MANAGED]”),默认JDBC
- 默认连接池:POOLED
3、属性(properties)
可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。【db.properties】
- 编写一个配置文件
db.propertiesdriver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF8 username=root password=1234qwer
- 在核心配置文件中引入
1、可以直接引入外部文件
2、可以在其中增加一些属性配置<?xml version="1.0" encoding="UTF8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--configuration核心配置文件--> <configuration> <!-- 引入外部配置文件--> <properties resource="db.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/jjl/dao/UserMapper.xml"/> </mappers> </configuration>
3、如果外部文件和内部增加的配置一样,则外部文件中配置的优先级高于内部增加的配置。<?xml version="1.0" encoding="UTF8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--configuration核心配置文件--> <configuration> <!-- 引入外部配置文件--> <properties resource="db.properties"> <property name="username" value="root"/> <property name="password" value="1234qwer"/> </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/jjl/dao/UserMapper.xml"/> </mappers> </configuration>
4、类型别名(typeAliases)
-
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
<!-- 给实体类起别名--> <typeAliases> <typeAlias type="com.jjl.pojo.User" alias="User"/> </typeAliases>
-
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author
<typeAliases> <package name="com.jjl.pojo"/> </typeAliases>
-
在实体类比较少时没使用第一种方式
-
在实体类比较多时,使用第二种方式
-
第一种可以自定义起别名,第二种不行(但可以通过给实体类添加注解来自定义别名:@Alias(“hello”))
五、设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true /false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true/ false | false |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J / LOG4J(3.5.9 起废弃) / LOG4J2 /JDK_LOGGING / COMMONS_LOGGING / STDOUT_LOGGING /NO_LOGGING | 未设置 |
········· | ······ | ····· | ······ |
六、其他
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- mybatis-generator-core
- mybatis-plus
- 通用mapper
七、映射器(mappers)
方式1
<mappers>
<mapper resource="com/jjl/dao/UserMapper.xml"/>
</mappers>
方式2:使用class文件绑定注册
<mappers>
<mapper class="com.jjl.dao.UserMapper"/>
</mappers>
方式3:使用扫描包镜像注册绑定
<mappers>
<!-- <mapper resource="com/jjl/dao/UserMapper.xml"/>-->
<!-- <mapper class="com.jjl.dao.UserMapper"/>-->
<package name="com.jjl.dao"/>
</mappers>
方式2和方式3注意点:
- 接口和它的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须在同一个包下
八、生命周期和作用域
生命周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题。
- SqlSessionFactoryBuilder:
- 一旦创建了 SqlSessionFactory,就不再需要它了。
- 局部变量
- SqlSessionFactory:
- 说白就是可以想象为:数据库连接池。
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- SqlSessionFactory 的最佳作用域是应用作用域。
- 最简单的就是使用单例模式或者静态单例模式。
- SqlSession:
连接到连接池的一个请求!
SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
用完后需要赶紧关闭,否则资源被占用!
九、解决属性名和字段名不一致的问题
1、测试问题
数据库中的字段
实体类
测试结果:
获取不到数据库中pwd的值
2、解决方法:
2.1、在sql语句中起别名
2.2、resultMap(结构集映射)
-
resultMap 元素是 MyBatis 中最重要最强大的元素
-
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
数据库:id name pwd
实体类:id name password
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace绑定一个对应的dao/mapper接口-->
<mapper namespace="com.jjl.dao.UserMapper">
<!-- 结果集映射-->
<resultMap id="UserMap" type="User">
<!-- column:数据库中的字段 property:对应实体类中的属性-->
<result column="id" property="id"/> <!--如果数据库字段名和实体类属性一样,则可以不单独做映射,所以该行可以省略-->
<result column="name" property="name"/> <!--如果数据库字段名和实体类属性一样,则可以不单独做映射,所以该行可以省略-->
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserById" resultMap="UserMap" parameterType="int">
select * from mybatis.user where id = #{id};
</select>
</mapper>
十、日志工厂
如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的助手
Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:
- SLF4J
- LOG4J(要掌握)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING (要掌握)
- NO_LOGGING
在mybatis中具体使用,需要设置中设定
1、STDOUT_LOGGING:标准日志输出
在mybatis核心配置文件(mybatis-config.xml)中,配置日志
<!-- 配置日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
6.2、Log4j
什么是Log4j
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,
- 我们也可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
1、先导入log4j的依赖包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、log4j.properties配置文件
log4j.rootLogger=DEBUG,console,file
log4j.additivity.org.apache=true
# 控制台(console)
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.ImmediateFlush=true
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n
# 日志文件(file)
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.ImmediateFlush=true
log4j.appender.file.Append=true
log4j.appender.file.File=./log/jjl.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.Statement=DEBUG
log4j.logger.java.ResultSet=DEBUG
log4j.logger.java.PreparedStatement=DEBUG
3、配置log4j为日志的实现
<!-- 配置日志-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4、log4j的使用
简单实用
1、在要使用Log4j类中,导包import org.apache.log4j.Logger;
2、日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserDaoTest.class);
3、日志级别
public void testLog4j(){
logger.info("info:进入testLog4j");
logger.debug("debug:进入testLog4j");
logger.error("error:进入testLog4j");
}
十一、分页
- 减少数据的处理量
使用Limit分页
select * from user limit #{startIndex},#{pageSize}
1、使用Mybatis实现分页
1、接口
List<User> getUserByLimit(Map<String,Integer> map);
2、Mapper.xml
<!-- 分页实现查询-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from user limit #{startIndex},#{pageSize}
</select>
3、测试
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
hashMap.put("startIndex",0);
hashMap.put("pageSize",3);
List<User> userByLimit = mapper.getUserByLimit(hashMap);
for (User user : userByLimit) {
System.out.println(user);
}
sqlSession.close();
}
2、RowBounds实现分页
不使用sql进行分页
1、接口
//rowbounds分页
List<User> getUserByRouBounds();
2、mapper.xml
<!-- rowbounds分页实现查询-->
<select id="getUserByRouBounds" resultMap="UserMap">
select * from user
</select>
3、测试
@Test
public void getUserByRowBounds() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
//Rowbounds实现
RowBounds rowBounds = new RowBounds(1, 3);
List<User> userList = sqlSession.selectList("com.jjl.dao.UserMapper.getUserByRouBounds",null,rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
3、分页插件
官方文档:https://pagehelper.github.io/docs/howtouse/
十二、使用注解开发
1、面向接口编程
大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
根本原因:解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得更容易,规范性更好
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;而各个对象之前的协作关系则成为系统设计的关键,小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容,面向接口编程就是指按照这种思想来编程。
关于接口的理解
- 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离)的分离。
- 接口的本身反映了系统设计人员对系统的抽象理解。
- 接口应有两类:
- 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
- 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
- 一个体有可能有多个抽象面,抽象体与抽象面是有区别的
2、案例测试
1、编写接口
package com.jjl.dao;
import com.jjl.pojo.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
}
2、绑定接口
在mybatis-config.xml中添加
<!-- 绑定接口-->
<mappers>
<mapper class="com.jjl.dao.UserMapper"/>
</mappers>
3、测试实现
import com.jjl.dao.UserMapper;
import com.jjl.pojo.User;
import com.jjl.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//底层使用的是反射机制
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
}
4、测试结果:
没法做字段映射(结构集映射),mybatis建议使用xml
Mybatis详细的底层分析
十三、使用注解实现CRUD
1、修改sqlSessionFactory 工具类
修改sqlSessionFactory 工具类为自动提交为自动提交,真实开发不建议修改使用
package com.jjl.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 java.io.IOException;
import java.io.InputStream;
//sqlSessionFactory 工具类
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(){
//sqlSessionFactory.openSession(true):自动提交,真实项目中尽量不要使用自动提交
return sqlSessionFactory.openSession(true);
}
}
2、编写接口和注解
这种使用注解实现CRUD只能用于简单sql语句
package com.jjl.dao;
import com.jjl.pojo.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
//指定条件查询
//方法有多个参数时,所有参数前面必须加上@Param("id")注解
@Select("select * from user where id=#{id} and name=#{name}")
User getUserById(@Param("id") int id,@Param("name") String name);
//插入数据
@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
int addUser(User user);
//修改数据
@Update("update user set name=#{name},pwd=#{password} where id=#{id}")
int UpdateUser(User user);
//删除数据
@Delete("delete from user where id = #{id}")
int deleteUser(@Param("id") int id);
}
3、测试
import com.jjl.dao.UserMapper;
import com.jjl.pojo.User;
import com.jjl.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
//查询全部数据
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//底层应用反射
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
//指定条件查询
@Test
public void test2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//底层应用反射
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.getUserById(2, "jjl");
System.out.println(user1);
sqlSession.close();
}
//添加数据
@Test
public void test3(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//底层应用反射
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int addUser = mapper.addUser(new User(9,"hell3","1234"));
if (addUser>0){
System.out.println("添加成功");
}
sqlSession.close();
}
//修改数据
@Test
public void test4(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//底层应用反射
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int addUser = mapper.UpdateUser(new User(9,"文盛","9999"));
if (addUser>0){
System.out.println("修改成功");
}
sqlSession.close();
}
//删除数据
@Test
public void test5(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.deleteUser(7);
if (i>0){
System.out.println("删除成功");
}
sqlSession.close();
}
}
4、关于@param()注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但是建议都加上
- 我们在SQL中引用的就是我们在@Param()中设定的属性名
在使用mybatis的时候我们会使用到#{}和${}这两个符号来为sql语句传参数
- #{}是预编译处理,是占位符,${}是字符串替换,是拼接符
- 使用#{}可以有效的防止sql注入,提高系统的安全性
十四、Lombok
Project Lombok 是一个 java 库,可自动插入您的编辑器和构建工具,为您的 java 增添趣味。
不要再编写getter 或 equals 方法,使用一个注释的类就有一个功能齐全的构建器、自动化您的日志记录变量。
使用步骤
1、在IDEA中安装Lombok插件
2、在项目中导入Lombok的jar包
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
3、在实体类上加注解
package com.jjl.pojo;
import lombok.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Getter
@Setter
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
十五、多对一的处理
学习用的SQL语句:
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);
1、测试环境搭建
1、导入lombok
2、新建实体类Teacher、Student
package com.jjl.pojo;
import lombok.Data;
@Data
public class Student {
private int id;
private String name;
//学生需要关联一个老师
private Teacher teacher;
}
package com.jjl.pojo;
import lombok.Data;
@Data
public class Teacher {
private int id;
private String name;
}
3、建立Mapper接口
package com.jjl.dao;
public interface StudentMapper {
}
package com.jjl.dao;
import com.jjl.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface TeacherMapper {
@Select("select * from teacher where id=#{tid}")
Teacher getTeacher(@Param("tid") int id);
}
4、建立Mapper.xml文件
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration核心配置文件-->
<mapper namespace="com.jjl.dao.StudentMapper">
</mapper>
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration核心配置文件-->
<mapper namespace="com.jjl.dao.TeacherMapper">
</mapper>
5、在核心配置文件(mybatis-config.xml)中绑定Mapper接口文件
<mappers>
<mapper class="com.jjl.dao.StudentMapper"/>
<mapper class="com.jjl.dao.TeacherMapper"/>
</mappers>
5、测试查询
import com.jjl.dao.TeacherMapper;
import com.jjl.pojo.Teacher;
import com.jjl.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
public class Ttest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
}
}
2、按照查询嵌套处理
需求:查询所有学生的信息,以及对应老师的信息
1、接口编写
package com.jjl.dao;
import com.jjl.pojo.Student;
import java.util.List;
public interface StudentMapper {
//查询所有学生的信息,以及对应老师的信息
/*
* 思路:
* 1、查询所有的学生信息
* 2、根据查询出来的学生的tid,寻找对应的老师
* */
List<Student> getStudent();
}
2、编写StudentMapper.xml
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration核心配置文件-->
<mapper namespace="com.jjl.dao.StudentMapper">
<!--查询所有学生的信息,以及对应老师的信息
思路:
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
-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id=#{id}
</select>
</mapper>
3、测试
@Test
public void testStudent(){
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();
}
3、按照结果嵌套处理
- 编写接口
List<Student> getStudent2();
- 编写核心sql处理
<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>
- 测试
@Test public void testStudent2(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> studentList = mapper.getStudent2(); for (Student student : studentList) { System.out.println(student); } sqlSession.close(); }
十六、一对多处理
1、环境搭建
-
实体类
studentpackage com.jjl.pojo; import lombok.*; @Data @ToString @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class Student { private int id; private String name; private int tid; }
teacher
package com.jjl.pojo; import lombok.*; import java.util.List; @Data @AllArgsConstructor @NoArgsConstructor @ToString @Getter @Setter public class Teacher { private int id; private String name; //一个老师拥有多个学生 private List<Student> students; }
-
编写接口
public interface TeacherMapper { //获取老师 List<Teacher> getTeacher(); }
-
编写sql
<?xml version="1.0" encoding="UTF8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--configuration核心配置文件--> <mapper namespace="com.jjl.dao.TeacherMapper"> <select id="getTeacher" resultType="Teacher"> select * from teacher; </select> </mapper>
-
测试
@Test public void test(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class); List<Teacher> teacher = mapper.getTeacher(); for (Teacher teachers : teacher) { System.out.println(teachers); } sqlSession.close(); }
2、按结果集查询
-
编写接口
public interface TeacherMapper { //获取指定老师下的所有学生及老师的信息 Teacher getTeacher(@Param("tid") int id); }
-
编写核心sql
<?xml version="1.0" encoding="UTF8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--configuration核心配置文件--> <mapper namespace="com.jjl.dao.TeacherMapper"> <!-- 按结果集嵌套查询--> <select id="getTeacher" resultMap="TeacherStudent"> select s.id sid,s.name sname,t.name tname,t.id tid from student s, teacher t where s.tid=t.id and t.id=#{tid} </select> <resultMap id="TeacherStudent" type="Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <!--复杂的属性需要单独处理,对象:association,集合:collection javaType="" 指定属性类型 集合中的泛型信息,我们使用oftype获取--> <collection property="students" ofType="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap> </mapper>
-
测试
@Test public void test(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class); Teacher teacher = mapper.getTeacher(1); System.out.println(teacher); /* 输出的结果: * Teacher(id=1, name=秦老师, * students=[ * Student(id=1, name=小明, tid=1), Student(id=2, name=小红, tid=1), * Student(id=3, name=小张, tid=1), Student(id=4, name=小李, tid=1), * Student(id=5, name=小王, tid=1) * ]) * */ sqlSession.close(); }
3、按照查询进行处理
- 编写接口
Teacher getTeacher2(@Param("tid") int id);
- 核心sql
<!-- 按子查询--> <select id="getTeacher2" resultMap="TeacherStudent2"> select * from mybatis.teacher where id=#{tid} </select> <resultMap id="TeacherStudent2" type="Teacher"> <collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/> </resultMap> <select id="getStudentByTeacherId" resultType="Student"> select * from student where tid=#{itd} </select>
- 测试
@Test public void test2(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class); Teacher teacher2 = mapper.getTeacher2(1); System.out.println(teacher2); sqlSession.close(); }
4、小结
- 关联 - association 【多对一】
- 集合 - collection 【一对多】
- javaType & ofType
- javaType:用来指定实体类中的属性的类型
- ofType:用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
注意点
- 保证SQL的可读性,尽量保证通俗易懂
- 注意一对多和多对一中,属性名和字段的问题!
- 如果问题不好排除错误,可以使用日志,建议使用Log4j