title: 1 mybatis
工具类— >组件---- > 框架 -----> 平台
软件的半成品,解决了软件开发过程当中的普适性问题,从而简化了开发步骤,提供了开发的效率。
- ORM(Object Relational Mapping)对象关系映射,将程序中的一个对象与表中的一行数据一一对应。
- ORM框架提供了持久化类与表的映射关系,在运行时参照映射文件的信息,把对象持久化到数据库中`。
存在大量的冗余代码。
手工创建 Connection、Statement 等。
手工将结果集封装成实体对象。
查询效率低,没有对数据访问进行过优化(Not Cache)。
- MyBatis本是Apache软件基金会的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了Google Code,并且改名为MyBatis 。2013年11月迁移到Github。
- MyBatis是一个优秀的基于Java的持久层框架,支持自定义SQL,存储过程和高级映射。
- MyBatis对原有JDBC操作进行了封装,几乎消除了所有JDBC代码,使开发者只需关注 SQL 本身。
- MyBatis可以使用简单的XML或Annotation来配置执行SQL,并自动完成ORM操作,将执行结果返回。
官方网站:http://www.mybatis.org/mybatis-3/
下载地址:https://github.com/mybatis/mybatis-3/releases/tag/mybatis-3.5.1
创建空的工程 |
---|
重点
】在pom.xml中引入相关依赖
<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.0modelVersion>
<groupId>com.qfgroupId>
<artifactId>hello-mybatisartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.21version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.12version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
dependencies>
project>
创建并配置mybatis-config.xml
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="MySqlDB">
<environment id="MySqlDB">
<transactionManager type="JDBC"/>
<dataSource type="org.apache.ibatis.datasource.pooled.PooledDataSourceFactory">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="xxxMapper.xml"/>
mappers>
configuration>
注意:mapper.xml默认建议存放在resources中,路径不能以/开头
重点
】create table t_users(
id int primary key auto_increment,
name varchar(50),
password varchar(50),
sex varchar(1),
birthday datetime,
registTime datetime
)default charset = utf8;
定义所需CURD操作的实体类
package com.glls.mybatis.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private String password;
private String sex;
private Date birthday;
private Date registTime;
}
根据所需DAO定义接口、以及方法
package com.qf.mybatis.mapper;
import com.qf.mybatis.pojo.User;
public interface UserMapper {
public User selectUserById(Integer id);
}
在resources目录中创建Mapper.xml文件
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.glls.mybatis.mapper.UserMapper">
<select id="selectUserById" resultType="com.glls.mybatis.pojo.User" >
select * from t_users where id = #{arg0}
select>
mapper>
将Mapper.xml注册到mybatis-config.xml中 这块配置 是写在主配置文件中
<mappers>
<mapper resource="UserMapper.xml">mapper>
mappers>
MyBatis的API操作方式
打开UserMapper接口 在这个接口身上 点右键 如下图操作
package com.glls.mybatis.mapper;
import com.glls.mybatis.pojo.User;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import static org.junit.Assert.*;
/**
* @date 2023/4/15
* @desc
* mybatis 入门案例
*
* 1.添加相关依赖
* 2.主配置文件 cv
* 需要注意 在主配置文件中的 映射文件的配置
* 3.创建Mapper 接口
*
* 4. 创建 与Mapper 接口 与之对应的映射文件
*
* 5. 相关API的使用
*/
public class UserMapperTest {
@Test
public void selectUserById() throws IOException {
//1. 加载mybatis 的主配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//2. 创建SqlSessionFactory 连接对象工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//3. 得到sqlSession 对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//4. 调用sqlSession 的方法
//User o = sqlSession.selectOne("com.glls.mybatis.mapper.UserMapper.selectUserById", 8);
//接口指向实现类对象 底层 肯定是 基于 接口 和 映射文件 创建了 实现类的对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectUserById(8);
System.out.println(user);
}
}
iBatis传统操作方式
package com.qf.mybatis.part1.basic;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class HelloMyBatis {
@Test
public void test2() throws IOException {
//1.获得读取MyBatis配置文件的流对象
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//2.构建SqlSession连接对象的工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂获得连接对象
SqlSession sqlSession = factory.openSession();
//4.通过连接对象直接调用接口中的方法
Object o = sqlSession.selectOne("com.qf.mybatis.part1.basic.UserMapper.selectUserById", 1);
System.out.println(o);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tXN6iqhY-1683019801698)(Pictures/image-20230415174042785.png)]
在pom.xml文件最后追加< build >标签,以便可以将xml文件复制到classes中,并在程序运行时正确读取。
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
includes>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.xmlinclude>
<include>**/*.propertiesinclude>
<include>**/*.iniinclude>
includes>
resource>
resources>
build>
对于mybatis-config.xml的核心配置中,如果存在需要频繁改动的数据内容,可以提取到properties中。
#db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
username=root
password=123456
修改mybatis-config.xml。
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties" />
<environments default="MySqlDB">
<environment id="MySqlDB">
<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="UserMapper.xml" />
mappers>
configuration>
为实体类定义别名,提高书写效率。
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties ... />
<typeAliases>
<package name="com.glls.mybatis.pojo"/>
typeAliases>
...
configuration>
pom.xml添加log4j依赖
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
创建并配置log4j.properties
# Global logging configuration 全局的日志级别为 warn
log4j.rootLogger=warn, Console
# MyBatis logging configuration... StudentMapper 这个包下的日志级别为 TRACE
log4j.logger.com.glls.mybatis.mapper.UserMapper=TRACE
# Console output...
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%5p %d [%t] - %m%n
级别 | 描述 |
---|---|
ALL LEVEL | 打开所有日志记录开关;是最低等级的,用于打开所有日志记录。 |
DEBUG | 输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。 |
INFO | 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。 |
WARN | 输出警告信息;表明会出现潜在错误的情形。 |
ERROR | 输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。 |
FATAL | 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。 |
OFF LEVEL | 关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。 |
重点
】标签:< select id=“” resultType=“” >
public interface UserMapper {
//使用原生参数绑定
public User selectUserByIdAndPwd(Integer id , String pwd);
}
<select id="selectUserByIdAndPwd" resultType="user">
SELECT * FROM t_users
WHERE id = #{arg0} AND password = #{arg1}
select>
<select id="selectUserByIdAndPwd" resultType="user">
SELECT * FROM t_users
WHERE id = #{param1} AND password = #{param2}
select>
import org.apache.ibatis.annotations.Param; //引入注解
public interface UserMapper {
//使用MyBatis提供的@Param进行参数绑定
public User selectUserByIdAndPwd(@Param("id") Integer id , @Param("pwd") String pwd);
}
<select id="selectUserByIdAndPwd" resultType="user">
SELECT * FROM t_users
WHERE id = #{id} AND password = #{password}
select>
public interface UserMapper {
//使用对象属性进行参数绑定
public User selectUserByUserInfo(User user);
}
<select id="selectUserByUserInfo" resultType="user">
SELECT * FROM t_users
WHERE id = #{id} AND password = #{password}
select>
public interface UserMapper {
public List<User> selectUsersByKeyword(@Param("keyword") String keyword);
}
<mapper namespace="com.qf.mybatis.part1.different.UserMapper">
<select id="selectUsersByKeyword" resultType="user">
SELECT * FROM t_users
WHERE name LIKE concat('%',#{keyword},'%')
select>
mapper>
标签:< delete id=“” parameterType=“” >
<delete id="deleteUser" parameterType="int">
DELETE FROM t_users
WHERE id = #{id}
delete>
标签:< update id=“” parameterType=“” >
<update id="updateUser" parameterType="user">
UPDATE t_users SET name=#{name}, password=#{password}, sex=#{sex}, birthday=#{birthday}
WHERE id = #{id}
update>
标签:< insert id=“” parameterType=“” >
<insert id="insertUser" parameterType="user">
INSERT INTO t_users VALUES(#{id},#{name},#{password},#{sex},#{birthday},NULL);
insert>
<insert id="insertUser" parameterType="user">
INSERT INTO t_users VALUES(#{id},#{name},#{password},#{sex},#{birthday},NULL);
INSERT INTO t_users VALUES(NULL,#{name},#{password},#{sex},#{birthday},NULL);
insert>
标签:< selectKey id=“” parameterType=“” order=“AFTER|BEFORE”>
CREATE TABLE `t_product` (
`productid` int(11) NOT NULL AUTO_INCREMENT,
`productname` varchar(100) DEFAULT NULL,
`brand` varchar(100) DEFAULT NULL,
PRIMARY KEY (`productid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
private Integer productId;
private String productName;
private String brand;
}
<mapper namespace="com.glls.mybatis.mapper.ProductMapper">
<insert id="insertProduct">
/*主键回填 在插入语句执行之后 查询刚刚插入的 记录 的 id 赋值给 productId */
<selectKey keyProperty="productId" resultType="int" order="AFTER" >
select last_insert_id()
selectKey>
insert into t_product (productId,productName,brand) values (null,#{productName},#{brand})
insert>
mapper>
CREATE TABLE `t_order2` (
`id` varchar(100) NOT NULL,
`name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
private String id;
private String name;
}
<mapper namespace="com.glls.mybatis.mapper.OrderMapper">
<insert id="insertOrder">
<selectKey keyProperty="id" resultType="string" order="BEFORE">
select REPLACE(UUID(),'-','')
selectKey>
insert into t_order2 (id,name) values (#{id},#{name});
insert>
mapper>
重点
】
Resource:用于获得读取配置文件的IO对象,耗费资源,建议通过IO一次性读取所有所需要的数据。
SqlSessionFactory:SqlSession工厂类,内存占用多,耗费资源,建议每个应用只创建一个对象。
SqlSession:相当于Connection,可控制事务,应为线程私有,不被多线程共享。
将获得连接、关闭连接、提交事务、回滚事务、获得接口实现类等方法进行封装。
package com.qf.mybatis.part1.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.InputStream;
public class MyBatisUtils {
//获得SqlSession工厂
private static SqlSessionFactory factory;
//创建ThreadLocal绑定当前线程中的SqlSession对象
private static final ThreadLocal<SqlSession> tl = new ThreadLocal<SqlSession>();
static {
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(is);
} catch (Exception e) {
e.printStackTrace();
}
}
//获得连接(从tl中获得当前线程SqlSession)
public static SqlSession openSession(){
SqlSession session = tl.get();
if(session == null){
session = factory.openSession();
tl.set(session);
}
return session;
}
//释放连接(释放当前线程中的SqlSession)
public static void closeSession(){
SqlSession session = tl.get();
session.close();
tl.remove();
}
//提交事务(提交当前线程中的SqlSession所管理的事务)
public static void commit(){
SqlSession session = openSession();
session.commit();
closeSession();
}
//回滚事务(回滚当前线程中的SqlSession所管理的事务)
public static void rollback(){
SqlSession session = openSession();
session.rollback();
closeSession();
}
//获得接口实现类对象
public static <T extends Object> T getMapper(Class<T> clazz){
SqlSession session = openSession();
return session.getMapper(clazz);
}
}
调用MyBatisUtils中的封装方法。
@Test
public void testUtils() {
try {
UserMapper userMapper = MyBatisUtils.getMapper(UserMapper.class);
userMapper.deleteUser(15);
MyBatisUtils.commit();
} catch (Exception e) {
MyBatisUtils.rollback();
e.printStackTrace();
}
}
重点
中的重点】MyBatis只能自动维护库表”列名“与”属性名“相同时的一一对应关系,二者不同时,无法自动ORM。
自动ORM失效 |
---|
在SQL中使用 as 为查询字段添加列别名,以匹配属性名。
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.glls.mybatis.mapper.ManagerMapper">
<select id="selectManagerByIdAndPwd" resultType="com.glls.mybatis.pojo.Manager">
select mgr_id as id,mgr_name as name,mgr_pwd as password from t_managers where mgr_id = #{id} and mgr_pwd=#{pwd}
select>
mapper>
通过< resultMap id=“” type=“” >映射,匹配列名与属性名。
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.glls.mybatis.mapper.ManagerMapper">
<resultMap id="managerResultMap" type="manager">
<id property="id" column="mgr_id">id>
<result property="name" column="mgr_name">result>
<result property="password" column="mgr_pwd">result>
resultMap>
<select id="selectManagerByIdAndPwd2" resultMap="managerResultMap">
select * from t_managers where mgr_id = #{id} and mgr_pwd=#{pwd}
select>
mapper>
重点
】实体间的关系:关联关系(拥有 has、属于 belong)
OneToOne:一对一关系(Passenger— Passport)
OneToMany:一对多关系(Employee — Department)
ManyToMany:多对多关系(Student — Subject)
Table建立外键关系 |
---|
Entity添加关系属性 |
---|
Mapper中将属性与列名对应 |
---|
SQL
DROP TABLE IF EXISTS `t_passenger`;
CREATE TABLE `t_passenger` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`sex` varchar(32) DEFAULT NULL,
`birthday` date DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='乘客';
/*Data for the table `t_passenger` */
insert into `t_passenger`(`id`,`name`,`sex`,`birthday`) values (1,'zs','男','2021-07-29'),(2,'lss','女','2021-07-28');
DROP TABLE IF EXISTS `t_passport`;
CREATE TABLE `t_passport` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nationality` varchar(32) DEFAULT NULL,
`expire` date DEFAULT NULL,
`passenger_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
/*Data for the table `t_passport` */
insert into `t_passport`(`id`,`nationality`,`expire`,`passenger_id`) values (1,'中国','2023-07-29',1),(2,'韩国','2023-07-28',2);
pojo
package com.glls.mybatis.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @date 2023/4/17
* @desc 乘客
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Passenger {
private Integer id;
private String name;
private String sex;
private Date birthday;
private Passport passport;
//private Integer passportId;
//
//private String nationality;
//
//private Date expire;
}
package com.glls.mybatis.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @date 2023/4/17
* @desc 护照
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Passport {
private Integer id;
private String nationality;
private Date expire;
private Integer passenger_id;
}
<mapper namespace="com.glls.mybatis.mapper.PassengerMapper">
<resultMap id="passengerResultMap" type="passenger">
<id property="id" column="id"/>
<result property="name" column="name" />
<result property="sex" column="sex" />
<result property="birthday" column="birthday" />
<association property="passport" javaType="passport">
<id property="id" column="passportId">id>
<result property="nationality" column="nationality">result>
<result property="expire" column="expire">result>
<result property="passenger_id" column="passenger_id">result>
association>
resultMap>
<select id="selectPassengerById" resultMap="passengerResultMap">
SELECT p1.id , p1.name , p1.sex , p1.birthday , p2.id as passportId , p2.nationality , p2.expire , p2.passenger_id
FROM t_passengers p1 LEFT JOIN t_passports p2
ON p1.id = p2.passenger_id
WHERE p1.id = #{id}
select>
mapper>
作业: 查询 护照的时候 把对应的乘客 也查询出来
SQL参考
DROP TABLE IF EXISTS `t_departments`;
CREATE TABLE `t_departments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`location` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
/*Data for the table `t_departments` */
insert into `t_departments`(`id`,`name`,`location`) values (1,'研发部','杭州'),(2,'市场部','上海');
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
private Integer id;
private String name;
private String location;
}
DROP TABLE IF EXISTS `t_employees`;
CREATE TABLE `t_employees` (
`eid` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`salary` double DEFAULT NULL,
`dept_id` int(11) DEFAULT NULL,
PRIMARY KEY (`eid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
/*Data for the table `t_employees` */
insert into `t_employees`(`eid`,`name`,`salary`,`dept_id`) values (1,'zs',88888,1),(2,'ls',77777,1);
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
private Integer eid;
private String name;
private Double salary;
private Integer dept_id;
}
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.glls.mybatis.mapper.DepartmentMapper">
<resultMap id="rs2" type="department">
<id property="id" column="id">id>
<result property="name" column="name">result>
<result property="location" column="location">result>
<collection property="emps" ofType="employee">
<id property="eid" column="eid">id>
<result property="name" column="ename">result>
<result property="salary" column="salary" />
<result property="dept_id" column="dept_id" />
collection>
resultMap>
<select id="findById" resultMap="rs2">
select t1.*,t2.eid,t2.name as ename,t2.salary,t2.dept_id from
t_departments t1 join t_employees t2
on t1.id = t2.dept_id where t1.id = #{id};
select>
mapper>
作业: 查询员工 同时 把 员工所属的部门信息查询出来
SQL
DROP TABLE IF EXISTS `t_students`;
CREATE TABLE `t_students` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`sex` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
/*Data for the table `t_students` */
insert into `t_students`(`id`,`name`,`sex`) values (1,'Tom','M'),(2,'Jack','M'),(3,'Marry','F'),(4,'Annie','F');
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer id;
private String name;
private String sex;
//学科
private List<Subject> subjects;
}
DROP TABLE IF EXISTS `t_subjects`;
CREATE TABLE `t_subjects` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`grade` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
/*Data for the table `t_subjects` */
insert into `t_subjects`(`id`,`name`,`grade`) values (1,'JavaSe','1'),(2,'JavaWeb','2'),(3,'FrameWork','3'),(4,'MicroService','4');
中间表
DROP TABLE IF EXISTS `t_stu_sub`;
CREATE TABLE `t_stu_sub` (
`student_id` int(11) DEFAULT NULL,
`subject_id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
/*Data for the table `t_stu_sub` */
insert into `t_stu_sub`(`student_id`,`subject_id`) values (1,1),(1,2),(2,1),(3,1);
建立第三张关系表 |
---|
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.glls.mybatis.mapper.StudentMapper">
<resultMap id="rs3" type="student">
<id property="id" column="id">id>
<result property="name" column="name">result>
<result property="sex" column="sex">result>
<collection property="subjects" ofType="subject">
<id property="id" column="subjectId">id>
<result property="name" column="subjectName">result>
<result property="grade" column="grade">result>
collection>
resultMap>
<select id="findAll" resultMap="rs3" >
select t1.*,t3.id as subjectId,t3.name as subjectName ,t3.grade from t_students t1
join t_stu_sub t2 on t1.id = t2.student_id
join t_subjects t3 on t2.subject_id = t3.id
select>
mapper>
作业 查询学科信息 同时 把学科的学生信息查询出来
一方,添加集合;多方,添加对象。
双方均可建立关系属性,建立关系属性后,对应的Mapper文件中需使用< ResultMap >完成多表映射。
持有对象关系属性,使用< association property=“dept” javaType=“department” >
持有集合关系属性,使用< collection property=“emps” ofType=“employee” >
重点
】MyBatis的映射文件中支持在基础SQL上添加一些逻辑操作,并动态拼接成完整的SQL之后再执行,以达到SQL复用、简化编程的效果。
准备表
DROP TABLE IF EXISTS `t_books`;
CREATE TABLE `t_books` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`author` varchar(32) DEFAULT NULL,
`publish` date DEFAULT NULL,
`sort` int(11) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
/*Data for the table `t_books` */
insert into `t_books`(`id`,`name`,`author`,`publish`,`sort`) values (8,'springcloud','java','2022-07-08',3),(16,'springboot','尤雨曦2','2021-10-22',NULL),(18,'springmvc','xxxooo','2022-04-14',2),(21,'springmvc','xxxooo','2022-04-14',2),(22,'spring','xxx','2022-07-08',NULL),(23,'springmvc','123','2022-07-08',NULL),(25,'springcloud','xxx',NULL,NULL),(26,'vue3','尤雨曦',NULL,NULL),(27,'vue','尤雨曦',NULL,NULL);
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
private Integer id;
private String name;
private String author;
private Date publish;
private Integer sort;
}
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.glls.mybatis.mapper.BookMapper">
<sql id="books_field">
SELECT id,name,author,publish,sort
sql>
<select id="selectBookByCondition" resultType="com.glls.mybatis.pojo.Book">
<include refid="books_field">include>
from t_books
select>
mapper>
<select id="selectBookByCondition" resultType="com.glls.mybatis.pojo.Book">
<include refid="books_field">include>
from t_books
<where>
<if test="name!=null and name != ''">
name=#{name}
if>
<if test="author!=null and author != '' ">
and author=#{author}
if>
where>
select>
<select id="selectBookByCondition" resultType="com.glls.mybatis.pojo.Book">
SELECT id , name , author , publish , sort
FROM t_books
<where>
<if test="id != null">
id = #{id}
if>
<if test="name != null">
and name = #{name}
if>
<if test="author != null">
and author = #{author}
if>
<if test="publish != null">
and publish = #{publish}
if>
<if test="sort != null">
and sort = #{sort}
if>
where>
select>
<update id="updateBookByCondition">
UPDATE t_books
<set>
<if test="name != null">
name = #{name} ,
if>
<if test="author != null">
author = #{author} ,
if>
<if test="publish != null">
publish = #{publish} ,
if>
<if test="sort != null">
sort = #{sort} ,
if>
set>
WHERE id = #{id}
update>
< trim prefix=“” suffix=“” prefixOverrides=“” suffixOverrides=“” >代替< where > 、< set >
<select id="selectBookByCondition" resultType="com.qf.mybatis.day2.dynamic.Book">
SELECT id,name,author,publish,sort
FROM t_books
<trim prefix="WHERE" prefixOverrides="AND|OR">
<if test="id != null">
and id = #{id}
if>
<if test="name != null">
and name = #{name}
if>
<if test="author != null">
and author = #{author}
if>
<if test="publish != null">
and publish = #{publish}
if>
<if test="sort != null">
and sort = #{sort}
if>
trim>
select>
<update id="updateBookByCondition">
UPDATE t_books
<trim prefix="SET" suffixOverrides=",">
<if test="name != null">
name = #{name} ,
if>
<if test="author != null">
author = #{author} ,
if>
<if test="publish != null">
publish = #{publish} ,
if>
<if test="sort != null">
sort = #{sort}
if>
trim>
WHERE id = #{id}
update>
批量删除
<delete id="deleteBookByIds">
DELETE FROM t_books
WHERE id IN
<foreach collection="list" open="(" separator="," close=")" item="id" index="i">
#{id}
foreach>
delete>
-----批量添加
<insert id="addBooks">
insert into t_books values
<foreach collection="list" item="book" separator=",">
(null,#{book.name},#{book.author},#{book.publish},#{book.sort})
foreach>
insert>
参数 | 描述 | 取值 |
---|---|---|
collection | 容器类型 | list、array、map |
open | 起始符 | ( |
close | 结束符 | ) |
separator | 分隔符 | , |
index | 下标号 | 从0开始,依次递增 |
item | 当前项 | 任意名称(循环中通过 #{任意名称} 表达式访问) |
重点
】内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,便于二次快速访问。
无缓存:用户在访问相同数据时,需要发起多次对数据库的直接访问,导致产生大量IO、读写硬盘的操作,效率低下 |
---|
有缓存:首次访问时,查询数据库,将数据存储到缓存中;再次访问时,直接访问缓存,减少IO、硬盘读写次数、提高效率 |
---|
SqlSession级别的缓存,同一个SqlSession的发起多次同构查询,会将数据保存在一级缓存中。
在sqlsession 中有一个数据结构 是map 结构, 这个区域就是一级缓存区域,一级缓存区域中的 key 是由 sql 语句 方法参数 statement 等 组成的一个唯一值 对应的 value 就是 缓存内容 , 一级缓存 map 的生命周期 和当前 sqlSession 一致
SqlSessionFactory级别的缓存,同一个SqlSessionFactory构建的SqlSession发起的多次同构查询,会将数据保存在二级缓存中。
二级缓存指的是 同一个 namespace 下的 mapper 二级缓存的数据结构 也是一个 map 二级缓存 需要 手动开启
< settings >是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行行为,其他详细配置可参考官方文档。
<configuration>
<properties .../>
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
<typeAliases>typeAliases>
configuration>
<mapper namespace="com.glls.mybatis.mapper.BookMapper">
<cache />
<select id="selectBookById" resultType="com.glls.mybatis.pojo.Book">
select * from t_books where id = #{id}
select>
mapper>
@Test
public void test(){
//测试一级缓存
BookMapper mapper = MyBatisUtils.getMapper(BookMapper.class);
System.out.println("第一次查询");
Book book = mapper.selectBookById(29);
System.out.println("第二次查询");
Book book1 = mapper.selectBookById(29);
System.out.println(book1);
MyBatisUtils.commit();
System.out.println("第三次查询");
BookMapper mapper1 = MyBatisUtils.getMapper(BookMapper.class);
Book book3 = mapper1.selectBookById(29);
}
增删改操作 会清空二级缓存
@Test
public void test2(){
//测试 二级缓存
BookMapper mapper = MyBatisUtils.getMapper(BookMapper.class);
System.out.println("第一次查询,从数据库取数据");
Book book = mapper.selectBookById(29);
System.out.println("第二次查询,走一级缓存");
Book book1 = mapper.selectBookById(29);
MyBatisUtils.commit();
System.out.println("第三次查询,走的是二级缓存");
BookMapper mapper1 = MyBatisUtils.getMapper(BookMapper.class);
Book book3 = mapper1.selectBookById(29);
MyBatisUtils.commit();
BookMapper mapper3 = MyBatisUtils.getMapper(BookMapper.class);
mapper3.deleteBookById(16);
MyBatisUtils.commit();
System.out.println("第四次查询,走的还是二级缓存");
BookMapper mapper2 = MyBatisUtils.getMapper(BookMapper.class);
Book book4 = mapper2.selectBookById(29);
}
Druid 是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池、插件框架和 SQL 解析器组成。该项目主要是为了扩展 JDBC 的一些限制,可以让程序员实现一些特殊的需求,比如向密钥服务请求凭证、统计 SQL 信息、SQL 性能收集、SQL 注入检查、SQL 翻译等,程序员可以通过定制来实现自己需要的功能。
测试执行申请归还连接 1,000,000(一百万)次总耗时性能对比。
环境 | 版本 |
---|---|
OS | OS X 10.8.2 |
CPU | Intel i7 2GHz 4 Core |
JVM | Java Version 1.7.0_05 |
JDBC-Conn Pool | 1 Thread | 2 threads | 5 threads | 10 threads | 20 threads | 50 threads |
---|---|---|---|---|---|---|
Druid | 898 | 1,191 | 1,324 | 1,362 | 1,325 | 1,459 |
tomcat-jdbc | 1,269 | 1,378 | 2,029 | 2,103 | 1,879 | 2,025 |
DBCP | 2,324 | 5,055 | 5,446 | 5,471 | 5,524 | 5,415 |
BoneCP | 3,738 | 3,150 | 3,194 | 5,681 | 11,018 | 23,125 |
jboss-datasource | 4,377 | 2,988 | 3,680 | 3,980 | 32,708 | 37,742 |
C3P0 | 10,841 | 13,637 | 10,682 | 11,055 | 14,497 | 20,351 |
Proxool | 16,337 | 16,187 | 18,310(Ex) | 25,945 | 33,706(Ex) | 39,501 (Ex) |
引入Druid依赖
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.16version>
dependency>
MyDruidDataSourceFactory并继承PooledDataSourceFactory,并替换数据源。
package com.qf.mybatis.part2.utils;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
public class MyDruidDataSourceFactory extends PooledDataSourceFactory {
public MyDruidDataSourceFactory() {
this.dataSource = new DruidDataSource();//替换数据源
}
}
mybatis-config.xml中连接池相关配置。
<dataSource type="com.qf.mybatis.part2.utils.DruidDataSourceFactory">
<property name="driverClass" value="${driver}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
注意:< property name=“属性名” />属性名必须与com.alibaba.druid.pool.DruidAbstractDataSource中一致。
PageHelper是适用于MyBatis框架的一个分页插件,使用方式极为便捷,支持任何复杂的单表、多表分页查询操作。
官方网站:https://pagehelper.github.io/
下载地址:https://github.com/pagehelper/Mybatis-PageHelper
PageHelper中提供了多个分页操作的静态方法入口。
pom.xml中引入PageHelper依赖。
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.1.10version>
dependency>
在MyBatis-config.xml中添加< plugins >。
<configuration>
<typeAliases>typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">plugin>
plugins>
<environments>...environments>
configuration>
使用PageHelper提供的静态方法设置分页查询条件。
@Test
public void testPagehelper(){
UserMapper userMapper = MyBatisUtils.getMapper(UserMapper.class);
PageHelper.startPage(1,2);//使用PageHelper设置分页条件
List<User> users = userMapper.selectAllUsers();
for(User user : users){
System.out.println(user);
}
}
PageInfo对象中包含了分页操作中的所有相关数据。
PageInfo结构图 |
---|
使用PageInfo保存分页查询结果。
@Test
public void test(){
BookMapper mapper = MyBatisUtils.getMapper(BookMapper.class);
//在执行 查询语句之前
//传入 页码 和 每页记录数
PageHelper.startPage(2,3);
//查询方法 要放在 PageHelper.startPage(2,2); 后面
List<Book> books = mapper.selectBookByCondition(null);
//把查询结果封装在 pageInfo 对象中
PageInfo<Book> bookPageInfo = new PageInfo<>(books);
//books.forEach(System.out::println);
System.out.println(bookPageInfo);
}
- 只有在PageHelper.startPage()方法之后的第一个查询会有执行分页。
- 分页插件不支持带有“for update”的查询语句。
- 分页插件不支持“嵌套查询”,由于嵌套结果方式会导致结果集被折叠,所以无法保证分页结果数量正确。。
使用Servlet+JSP+MyBatis+分页插件,完成分页查询功能。
以下内容并非必备知识,了解即可。
通过在接口中直接添加MyBatis注解,完成CRUD。
<mappers>
<package name="com.glls.mybatis.mapper"/>
mappers>
public interface UserMapper {
@Select("SELECT * FROM t_users WHERE id = #{id}")
public User selectUserById(Integer id);
@Select("SELECT * FROM t_users WHERE id = #{id} AND password = #{pwd}")
public User selectUserByIdAndPwd_annotation(@Param("id") Integer id, @Param("pwd") String password);
}
@Delete(value = "DELETE FROM t_users WHERE id = #{id}")
public int deleteUser(Integer id);
@Update("UPDATE t_users SET name = #{name} , password = #{password} , salary = #{salary} , birthday = #{birthday} WHERE id = #{id}")
public int updateUser(User user);
@Insert("INSERT INTO t_users VALUES(#{id},#{name},#{password},#{salary},#{birthday},null)")
public int insertUser(User user);
@Options(useGeneratedKeys = true , keyProperty = "id") // 自增key,回显主键为id
@Insert("INSERT INTO t_users VALUES(#{id},#{name},#{password},#{salary},#{birthday},null)")
public int insertUserGeneratedKeys(User user);
${attribute} 属于字符串拼接SQL,而非预编译占位符,会有注入攻击问题,不建议在常规SQL中使用,常用于可解决动态生降序问题。
public List<User> selectAllUsers1(User user); // ${name} ${id} 可获取user中的属性值
public List<User> selectAllUsers2(@Param("rule") String rule); //必须使用@Param否则会作为属性解析
<select id="selectAllUsers1" resultType="user">
SELECT * FROM t_users
WHERE name = '${name}' or id = ${id}
select>
<select id="selectAllUsers2" resultType="user">
SELECT * FROM t_users
ORDER BY id ${rule}
select>
User user = new User(....);
List<User> ulist1 = userDAO.selectAllUsers1(user); //调用时传入user对象
List<User> ulist2 = userMapper.selectAllUsers2("desc"); //调用时传入asc | desc
<select id="selectUsersByKeyword" resultType="user">
SELECT * FROM t_user
WHERE name = '${name}'
select>
注入攻击,拼接的内容,改变了原sql语义,被攻击! |
---|
思路:查询部门信息时,及联查询所属的员工信息。
- DepartmentDao接口中定义selectDepartmentById,并实现Mapper。
- EmployeeDao接口中定义selectEmployeesByDeptId,并实现Mapper,
- 当selectDepartmentById被执行时,通过< collection >调用selectEmployeesByDeptId方法,并传入条件参数。
定义selectEmployeesByDeptId,并书写Mapper,实现根据部门ID查询员工信息
public interface EmployeeDao {
/**
* 根据部门编号查询员工信息
* @param id 部门编号
* @return 该部门中的所有员工
*/
public List<Employee> selectEmployeeByDeptId(Integer id);
}
<mapper namespace="com.qf.mybatis.part2.one2many.EmployeeDao">
<select id="selectEmployeeByDeptId" resultType="com.glls.mybatis.pojo.Employee">
select * from t_employees where dept_id = #{id}
select>
mapper>
定义selectDepartmentById,并书写Mapper,实现根据部门ID查询部门信息,并及联查询该部门员工信息
public interface DepartmentMapper {
/**
* 根据id查询部门信息 同时 把部门的员工也查出来
* @param id
* @return
*/
public Department findDepartmentById(Integer id);
}
<mapper namespace="com.glls.mybatis.mapper.DepartmentMapper">
<resultMap id="departmentResultMap" type="department">
<id property="id" column="id">id>
<result property="name" column="name">result>
<result property="location" column="location">result>
<collection property="emps" ofType="employee" column="id" select="com.glls.mybatis.mapper.EmployeeMapper.selectEmployeeByDeptId">collection>
resultMap>
<select id="findDepartmentById" resultMap="departmentResultMap">
SELECT id , name , location
FROM t_departments
WHERE id = #{id}
select>
mapper>
mybatis-config.xml中开启延迟加载
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
settings>
比较实用
使用mybatis逆向工程生成代码的方式有多种 ,但是生成的代码都差不多,所以这里咱们介绍一种,生成代码的细节 咱不用管,主要掌握生成的代码如何使用
这里咱们使用mybatis的插件 生成的代码
生成代码如下
ByDeptId(Integer id);
}
```xml
定义selectDepartmentById,并书写Mapper,实现根据部门ID查询部门信息,并及联查询该部门员工信息
public interface DepartmentMapper {
/**
* 根据id查询部门信息 同时 把部门的员工也查出来
* @param id
* @return
*/
public Department findDepartmentById(Integer id);
}
<mapper namespace="com.glls.mybatis.mapper.DepartmentMapper">
<resultMap id="departmentResultMap" type="department">
<id property="id" column="id">id>
<result property="name" column="name">result>
<result property="location" column="location">result>
<collection property="emps" ofType="employee" column="id" select="com.glls.mybatis.mapper.EmployeeMapper.selectEmployeeByDeptId">collection>
resultMap>
<select id="findDepartmentById" resultMap="departmentResultMap">
SELECT id , name , location
FROM t_departments
WHERE id = #{id}
select>
mapper>
mybatis-config.xml中开启延迟加载
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
settings>
比较实用
使用mybatis逆向工程生成代码的方式有多种 ,但是生成的代码都差不多,所以这里咱们介绍一种,生成代码的细节 咱不用管,主要掌握生成的代码如何使用
这里咱们使用mybatis的插件 生成的代码
生成代码如下
mapper 接口中生成了 常用的 方法, xml 映射文件中 生成了对应的 statement