0、Mybatis简介&实验环境
MyBatis 是一款优秀的持久层框架。
持久层:对应SpringMVC中的DAO层(Dao层,Service层,Controller层)
持久层的意义:数据持久化,内存存在断电既失并且内存空间相对较小的问题,因此将数据持久化可以防止数据的丢失。
Mybatis的优点:
- sql和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql。
环境:
- JDK1.8 Mysql 5.7 maven 3.6.1 IDEA
前序学习:
- JDBC Mysql java基础 Maven Junit
1、入门CRUD(第一个程序)
1.1、搭建实验环境
1.1.1、搭建数据库
CREATE DATABASE `mybatis`;
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=utf8;
insert into `user`(`id`,`name`,`pwd`) values (1,'狂神','123456'),(2,'张三','abcdef'),(3,'李四','987654');
-- 查询一下是否成功创建
SELECT * FROM user
1.1.2、新建项目
1、新建一个普通的maven项目
2、设置maven为本地
1.1.3、导入依赖
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<!-- <scope>test</scope>-->
</dependency>
</dependencies>
1.2、编写代码
1.2.1、编写MyBatis工具类
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;
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession连接
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}
1.2.2、resources配置文件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?serverTimezone=GMT&useUnicode=true&characterEncoding=utf8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="hsp"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/wuning/dao/UserMapper.xml"/>
</mappers>
</configuration>
1.2.3、实体类-User
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 String getName() {
return name;
}
public String getPwd() {
return pwd;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
1.2.4、接口UserMapper
public interface UserMapper {
//可以避免JDBC代码和手动设置参数
List<User> getUserList();
根据ID查询用户
User getUserById(int id);
//'insert一个用户
int addUser(User user);
//修改用户
int updateUser(User user);
//删除一个用户
int deleteUser(int id);
}
1.2.5、编写对应的UserMapper.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="org.wuning.dao.UserMapper">
<select id="getUserList" resultType="org.wuning.pojo.User">
select * from mybatis.user
</select>
<select id="getUserById" parameterType="int" resultType="org.wuning.pojo.User">
select * from mybatis.user where id =#{id}
</select>
<insert id="addUser" parameterType="org.wuning.pojo.User">
insert into mybatis.user (id, name, pwd) VALUES (#{id},#{name},#{pwd});
</insert>
<update id="updateUser" parameterType="org.wuning.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>
</mapper>
1.2.6、编写测试类
@Test
public void test(){
//第一步获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
//方式一:执行sql getMapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
List<User> userList = userDao.getUserList();
// //方式2
List<User> userList = sqlSession.selectList("org.wuning.dao.UserDao.getUserList");
for (User user : userList){
System.out.println(user);
}
}catch (Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
}
@Test
public void getUserById(){
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(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int hh = mapper.addUser(new User(4, "hh", "123456"));
if (hh>0){
System.out.println("插入成功");
}
//提交事务
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.updateUser(new User(4,"hehe","123213"));
sqlSession.commit();
sqlSession.close();
}
@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser(4);
sqlSession.commit();
sqlSession.close();
}
1.2.7、常见问题
1.2.7.1、搭建环境中的路径问题
新建项目的时候需要添加GroupID(我设置的org.wuning),创建出的项目中可能不会自动生成,需要手动创建对应路径,并且把上述代码文件均放到该路径下。我出现了java文件可以找到,但是UserMapper.xml文件找不到的问题。
mybatis-config.xml中
resource的路径需要一致
1.2.7.2、数据库配置
数据库无法连接
1、账号密码需要修改成自己的,在mybatis-config.xml中修改username和password
2、可以正常运行,但是UserMapper.xml文件中mybatis出现标红,不影响程序执行,可以通过右上角database将idea连接至数据库即可识别。
select * from mybatis.user
1.2.8 课堂练习(多条件查找)
1.2.8.1、 注解实现@param
1、接口声明
User selectUserByNp(@Param("username") String username, @Param("pwd") String pwd);
2、编写xml
<select id="selectUserByNp" resultType="org.wuning.pojo.User">
select * from mybatis.user where name =#{username} AND pwd=#{pwd};
</select>
3、测试类
@Test
public void getUserByNamePwd(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectUserByNp("狂神","123456");
System.out.println(user);
sqlSession.close();
}
1.2.8.2、 万能的Map
1、接口声明
User selectUserByNP2(Map<String,Object> map);
2、编写xml
<select id="selectUserByNP2" resultType="org.wuning.pojo.User">
select * from mybatis.user where name = #{username} and pwd = #{pwd}
</select>
3、测试类
@Test
public void getUserMap(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("username","狂神");
map.put("pwd","123456");
User user = mapper.selectUserByNP2(map);
System.out.println(user);
sqlSession.close();
}
1.2.8.3、总结:参数过多,直接使用Map实现,参数比较少,直接传递参数@param
1.2.9 思考题(模糊查询)
其中一种
//接口
List<User> selectlike(String ks);
//测试
@Test
public void getUserLike(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
String wildcardname = "狂";
List<User> users = mapper.selectlike(wildcardname);
System.out.println(users);
sqlSession.close();
}
//xml
<select id="selectlike" resultType="org.wuning.pojo.User">
select * from mybatis.user where name like "%"#{ks}"%"
</select>
1.2.10 总结
- 所有的增删改操作都需要提交事务!
session.commit(); //提交事务,重点!不写的话不会提交到数据库
- 接口所有的普通参数,尽量都写上@Param参数,尤其是多个参数时,必须写上!
User selectUserByNp(@Param("username") String username, @Param("pwd") String pwd);
- 有时候根据业务的需求,可以考虑使用map传递参数!
- 为了规范操作,在SQL的配置文件中,我们尽量将Parameter参数和resultType都写上!
<select id="getUserById" parameterType="int" resultType="org.wuning.pojo.User">
2、配置解析
新建一个Module
2.1、环境配置(environments)
MyBatis可以配置成适应多种环境,但每个SqlSessionFactory实例只能选择一种环境。
<environments default="development"> 默认development
<environment id="development"> 环境1 development
<transactionManager type="JDBC"/> 事务管理器就是JDBC
<dataSource type="POOLED"> 连接池:POOLED
unpooled:这个数据源的实现只是每次被请求时打开和关闭连接。
pooled:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,
这是一种使得并发 Web 应用快速响应请求的流行处理方式。
jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,
容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useUnicode=true&characterEncoding=utf8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="hsp"/>
</dataSource>
</environment>
<environment id="test"> 环境2 test
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useUnicode=true&characterEncoding=utf8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="hsp"/>
</dataSource>
</environment>
</environments>
2.2、引入资源方式 mappers(映射器)
mybatis-config.xml
<mappers>
<mapper resource="org/wuning/dao/UserMapper.xml"/>
</mappers>
UserMapper.xml
<mapper namespace="org.wuning.dao.UserMapper">
<select id="getUserList" resultType="org.wuning.pojo.User">
select * from mybatis.user
</select>
</mapper>
2.3、 Properties (属性)
数据库这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useUnicode=true&characterEncoding=utf8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="hsp"/>
1、在资源目录下新建一个db.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useUnicode=true&characterEncoding=utf8&useSSL=false
jdbc.username=root
jdbc.password=hsp
注意serverTimezone中不能出现;
会自动识别;
为结束 ,本来的&
还原成&
2、将文件导入properties 配置文件
<configuration>
<!--导入properties文件-->
<properties resource="db.properties"/> </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="mapper/UserMapper.xml"/>
</mappers>
</configuration>
2.4、类型别名(typeAliases)
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
<!--配置别名,注意顺序-->
<typeAliases>
<typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
2.5、设置
<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>
2.6、类型处理器
- 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
- 你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。【了解即可】
2.7、对象工厂
- MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。
- 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过有参构造方法来实例化。
- 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。【了解即可】
2.8、生命周期与作用域
这里面的每一个Mapper,就代表一个具体的业务!
SqlSessionFactoryBuilder:
- 但创建了SqlSessionFactory,就不再需要它了
- 局部变量
SqlSessionFactory:
- 说白了就是可以想象为:数据库连接池
- SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- 因此SqlSessionFactory的最佳作用域是应用作用域。
- 最简单的就是使用单例模式或者静态单例模式。
SqlSession:
- 连接到连接池的一个请求!
- SqlSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
- 用完之后需要赶紧关闭,否则资源被占用!
3、属性名与字段名不一致
之前的操作中数据库中密码的字段名是pwd,User类中的密码也是pwd,现在为了模拟字段名不一致的情况,将User类中的pwd属性名改成 password
public class User {
private int id;
private String name;
private String password;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
运行程序发现
其中password没有成功赋值,因为名称没有对应。
3.1、 取别名
<select id="getUserById" parameterType="int" resultType="org.wuning.pojo.User">
select id,name,pwd as password from mybatis.user where id =#{id}
</select>
3.2 、使用结果集映射->ResultMap【推荐】
<resultMap id="UserMap" type="org.wuning.pojo.User">
<!-- id为主键 -->
<id column="id" property="id"/>
<!-- column是数据库表的列名 , property是对应实体类的属性名 -->
<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>
1、报错
Cause: org.xml.sax.SAXParseException; lineNumber: 5; columnNumber: 26; 1 字节的 UTF-8 序列的字节 1 无效。
编码错误,UTF8而不是UTF-8
<?xml version="1.0" encoding="UTF8" ?>
2、User类需要有属性对应的set函数,否则xml中会标红,但是不影响程序运行
4、日志工厂
Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:
- SLF4J
- Apache Commons Logging
- Log4j 2
- Log4j
- JDK logging
具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。
4.1、 Log4j
- Log4j是Apache的一个开源项目
- 通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件…
- 我们也可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
4.1.1、log4j环境配置
1、导入包
pom.xml文件下
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、相关配置
resources文件夹下创建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/kuang.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
3、mybatis-config.xml文件中配置log4j为日志的实现
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
运行之前的程序,查看控制台是否输出相关日志
4.1.2、log4j的简单使用
import org.apache.log4j.Logger;
public class UserDaoText {
static Logger logger=Logger.getLogger(UserDaoText.class);
@Test
public void testLog4j(){
logger.info("info:进入了testLog4j");
logger.debug("debug:进入了testLog4j");
logger.error("error:进入了testLog4j");
}
}
运行后Module下出现log文件夹
5、分页
5.1、回顾mysql分页
#语法
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。
5.2、Mybatis实现分页
- 创建接口
List<User> getUserByLimit(Map<String,Integer> map);
- 编写usermapper.xml中对应sql语句
(resultMap="UserMap"第三章中讲过,结果集映射,解决名字不一样的问题)
<select id="getUserByLimit" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>
3、编写测试类
@Test
public void getUserLimit() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Integer> map=new HashMap<>();
map.put("startIndex",1);
map.put("pageSize",2);
List<User> users=mapper.getUserByLimit(map);
System.out.println(users);
}
5.3、分页插件
6、使用注解开发
6.1、面向接口编程(抽象概念理解)
面向接口编程:解耦
,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好
- 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法.
- 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现
- 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题更多的体现就是对系统整体的架构
6.2、注解开发实现
步骤1
、添加注解
@Select("select id,name,pwd password from mybatis.user")
public List<User> getAllUser();
步骤2
、在mybatis的核心配置文件(mybatis.config)中注入mapper类
<mappers>
<mapper class="org.wuning.dao.UserMapper"/>
</mappers>
与xml开发对比
在xml开发中是注入.xml文件,现在用上述替换(注意之前/分割
,注解开发中是.分割
)
<mappers>
<mapper resource="org/wuning/dao/UserMapper.xml"/>
</mappers>
步骤3
、测试
@Test
public void testGetAllUser() {
SqlSession session = MybatisUtils.getSqlSession();
//本质上利用了jvm的动态代理机制
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.getAllUser();
for (User user : users){
System.out.println(user);
}
session.close();
}
本质:反射机制实现
底层:动态代理!
6.3、Mybatis详细的执行流程
6.4、注解实现CRUD
6.4.1、接口
//根据id查询用户
@Select("select * from mybatis.user where id = #{id}")
User selectUserById(@Param("id") int id);
//增加用户
@Insert("insert into mybatis.user(id,name,pwd) value (#{id},#{name},#{password})")
int addUser(User user);
//更新用户
@Update("update mybatis.user set name=#{name} ,pwd=#{pwd} where id=#{id}")
int updateUser(User user);
//根据id删除用
@Delete("delete from mybatis.user where id = #{id}")
int deleteUser(@Param("id")int id);
6.4.2、测试用例
//根据ID查找
@Test
public void testSelectUserById() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);
System.out.println(user);
session.close();
}
//新增User
@Test
public void insertUser(){
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(new User(5,"dsdasda","asdrwae"));
sqlSession.close();
}
//修改
@Test
public void testUpdateUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(2, "3333", "333");
mapper.updateUser(user);
session.close();
}
//删
@Test
public void testDeleteUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.deleteUser(5);
session.close();
}
6、一对多和多对一处理
6.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');
6.1.1、按查询嵌套处理
1、新建实体类Teacher,Student
2、建立Mapper接口
3、建立Mapper.XML文件
4、在核心配置文件中绑定注册我们的Mappera接口或者文件
5、测试查询是否能够成功!
1、Teacher类,Student类
@Data
public class Teacher {
private int id;
private String name;
}
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
2、建立Mapper接口
public interface StudentMapper {
//获取所有学生及对应老师的信息
List<Student> getStudents();
}
public interface TeacherMapper {
@Select("select * from mybatis.teacher where id = #{tid}")
Teacher getTeacher(@Param("tid") int id);
}
3、建立Mapper.XML文件
<!--
需求:获取所有学生及对应老师的信息
思路:
1. 获取所有学生的信息
2. 根据获取的学生信息的老师ID->获取该老师的信息
3. 思考问题,这样学生的结果集中应该包含老师,该如何处理呢,数据库中我们一般使用关联查询?
1. 做一个结果集映射:StudentTeacher
2. StudentTeacher结果集的类型为 Student
3. 学生中老师的属性为teacher,对应数据库中为tid。
多个 [1,...)学生关联一个老师=> 一对一,一对多
4. 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询
-->
<select id="getStudents" resultMap="StudentTeacher">
select * from mybatis.student
</select>
<resultMap id="StudentTeacher" type="org.wuning.pojo.Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名-->
<association property="teacher" column="tid" javaType="org.wuning.pojo.Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="org.wuning.pojo.Teacher">
select * from mybatis.teacher where id = #{id}
</select>
TeacherMapper.xml文件可用注解代替
4、在核心配置文件(mybatis-config)中绑定注册我们的Mapper接口或者文件
5、测试查询是否能够成功!
@Test
public void testGetStudents(){
SqlSession session = MybatisUtils.getSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> students = mapper.getStudents();
for (Student student : students){
System.out.println(
"学生名:"+ student.getName()
+"\t老师:"+student.getTeacher().getName());
}
}
6.1.2、按结果嵌套处理
方法2和方法1 的区别在于StudentMapper.xml
<!--
按查询结果嵌套处理
思路:
1. 直接查询出结果,进行结果集的映射
-->
<select id="getStudents2" 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="org.wuning.pojo.Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<!--关联对象property 关联对象在Student实体类中的属性-->
<association property="teacher" javaType="org.wuning.pojo.Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
6.1.3、注意点
学生类和老师类在创建的时候,需要有无参构造函数。
无无参构造函数的话,会发生如下错误。(大概是因为创建对象映射的时候,存在反射实例化,需要无参构造函数
)
### The error may exist in org/wuning/dao/StudentMapper.xml
### The error may involve org.wuning.dao.StudentMapper.getStudents
### The error occurred while handling results
### SQL: select * from mybatis.student
### Cause: org.apache.ibatis.reflection.ReflectionException: Error instantiating class org.wuning.pojo.Student with invalid types (int,String,Teacher) or values (1,小明,1). Cause: java.lang.IllegalArgumentException: argument type mismatch
或者使用lombok
6.2、一对多处理
6.2.1、按结果嵌套处理
1、创建Student类和Teacher类
@Data
public class Student {
private int id;
private String name;
private int tid;
}
@Data
public class Teacher {
private int id;
private String name;
//一个老师有多个学生
private List<Student> students;
}
2、创建TeacherMapper接口
public interface TeacherMapper {
//获取指定老师,及老师下的所有学生
public Teacher getTeacher(int id);
}
3、创建TeacherMapper.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">
<mapper namespace="org.wuning.dao.TeacherMapper">
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,s.name sname,t.name tname,t.id tid
from mybatis.student s,mybatis.teacher t
where s.tid = t.id and t.id = #{tid}
</select>
<resultMap id="TeacherStudent" type="org.wuning.pojo.Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="org.wuning.pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
</mapper>
4、注册Mapper
<mapper class="org.wuning.dao.TeacherMapper"/>
5、测试
@Test
public void testGetTeacher(){
SqlSession session = MybatisUtils.getSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher.getName());
System.out.println(teacher.getStudents());
}
6.2.2、按查询嵌套处理
区别 3、创建TeacherMapper.xml映射文件(没有实验,嵌套查询感觉存在回表的问题)
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from teacher where id = #{id}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<!--column是一对多的外键 , 写的是一的主键的列名-->
<collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from student where tid = #{id}
</select>
6.2.3、小结
注意编码方式encoding=“UTF8”
- 关联-association【多对一】
- 集合-collection【一对多】
- javaType & ofType
- JavaType用来指定实体类中属性的类型
- ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型!
7、动态SQL
7.1 搭建环境
7.1.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
7.1.2、创建实体类
@Data
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
7.1.3、创建BlogMapper接口
public interface BlogMapper {
//新增一个博客
int addBlog(Blog blog);
}
7.1.4、创建BlogMapper.xml
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.wuning.dao.BlogMapper">
<insert id="addBlog" parameterType="org.wuning.pojo.Blog">
insert into mybatis.blog (id, title, author, create_time, views)
values (#{id},#{title},#{author},#{createTime},#{views});
</insert>
</mapper>
7.1.5、在config.xml中注册
注册xml
<!--注册Mapper.xml-->
<mappers>
<mapper resource="org/wuning/dao/BlogMapper.xml"/>
</mappers>
添加一些设置
<settings>
<!-- 是否开启自动驼峰命名规则映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 标准日志工厂实现-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
7.1.6、测试
补充工具类
public class IDUtil {
public static String genId(){
return UUID.randomUUID().toString().replaceAll("-","");
}
@Test
public void test(){
System.out.println(IDUtil.genId());
System.out.println(IDUtil.genId());
}
}
测试
@Test
public void addInitBlog(){
SqlSession session = MybatisUtils.getSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDUtil.genId());
blog.setTitle("Mybatis如此简单");
blog.setAuthor("狂神说");
blog.setCreateTime(new Date());
blog.setViews(9999);
mapper.addBlog(blog);
blog.setId(IDUtil.genId());
blog.setTitle("Java如此简单");
mapper.addBlog(blog);
blog.setId(IDUtil.genId());
blog.setTitle("Spring如此简单");
mapper.addBlog(blog);
blog.setId(IDUtil.genId());
blog.setTitle("微服务如此简单");
mapper.addBlog(blog);
session.close();
}
7.2、if ,where语句
需求1:
根据作者名字和博客名字来查询博客!
如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询
select * from blog where title = #{title} and author = #{author}
1、编写接口类
//需求1
List<Blog> queryBlogIf(Map map);
2、编写SQL语句
<select id="queryBlogIf" 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 开头的,则它会剔除掉。
3、测试
@Test
public void testQueryBlogIf(){
SqlSession session = MybatisUtils.getSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("title","Mybatis如此简单");
map.put("author","狂神说");
List<Blog> blogs = mapper.queryBlogIf(map);
System.out.println(blogs);
session.close();
}
7.3、Set 更新操作
<!--注意set是用的逗号隔开-->
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id};
</update>
7.4、choose(满足部分条件)
有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句
<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>
7.5、SQL片段(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="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="if-title-author"></include>
<!-- 在这里还可以引用其他的 sql 片段 -->
</where>
</select>
注意:
①、最好基于 单表来定义 sql 片段,提高片段的可重用性
②、在 sql 片段中不要包括 where
7.6、Foreach
将数据库中前三个数据的id修改为1,2,3;
需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from blog
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from blog where 1=1 and (id=1 or id=2 or id=3)
-->
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
HashMap map = new HashMap();
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
map.put("ids",ids);