myBatis笔记
1. java项目的基本三层架构
2. 框架和库的区别
- 框架: 是一整套技术解决方案
- 库:是对原有技术的封装 让操作更加简单而已
3.Mybatis的介绍
- MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github,通俗说法Ibatis3 = MyBatis
- iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
- MyBatis是一个数据持久层(ORM)框架。把实体类和SQL语句之间建立了映射关系,是一种半自动化的ORM实现
- MyBatis的优点:
- 减少代码量
- 基于SQL语法,简单易学
- 能了解底层组装过程
- SQL语句封装在配置文件中,便于统一管理与维护,降低了程序的耦合度
- 程序调试方便
4. MyBatis的获取
- 第一种方式 从官网(也是跳转到github)
- 第二种方式 github
5. mybatis的目录介绍
6. Mybatis操作数据库
6.1. 环境搭建
6.1.1. 建库建表
create database mybatis;
use mybatis;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` date DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性别',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
6.1.2. 插入数据
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`)
values (1,'张无忌',NULL,'2',NULL),
(10,'张甲吉','2014-07-10','1','北京'),(16,'谢逊',NULL,'1','北京'),
(22,'珠珠',NULL,'1','深圳'),(24,'周芷若',NULL,'1','广州'),
(25,'赵敏',NULL,'1','上海'),(26,'小昭',NULL,NULL,NULL);
6.1.3. 创建java项目
6.1.4. 导入jar包(新建lib)
导入mybatis和mybatis的依赖包
导入mysql的驱动包
注意: 直接导入mybatis 和 mysql驱动包 2个包就可以了 但是建议把所有的包都导入进去
6.1.5. 创建实体类:User
package com.shangma.cn.entity;
import java.util.Date;
public class User {
private int id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", sex='" + sex + '\'' +
", birthday=" + birthday +
", address='" + address + '\'' +
'}';
}
}
6.1.6. 编写主配置文件:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--mybaits全局配置文件的约束-->
<!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">
<!--jdbc的事务管理 交给mybatis管理 -->
<transactionManager type="JDBC"/>
<!--数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="rootroot"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--<mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
</mappers>
</configuration>
6.1.7. 编写Mapper映射文件 :UserMapper.xml
<?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="">
</mapper>
6.1.8. 基本目录结构如下
6.2. 增删改查
6.2.1. 根据id查询
6.2.1.1. 修改映射文件:UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
添加的内容如下
-->
<!--
namespace 表示命名空间 对sql的操作进行分类化管理 有点类似包管理
-->
<mapper namespace="huige">
<!--是根据id查询用户
select标签表示查询
id属性: 要求在一个namespace当中唯一 id随意写 但是要唯一
parameterType表示参数的java类型
resultType 表示查询后的返回值java类型
#{id} 表示占位符 传过来是什么 id就等于什么 如果是基本数据类型 这个id可以随意写
-->
<select id="findUserById" parameterType="int" resultType="com.shangma.cn.entity.User">
select * from user where id=#{id}
</select>
</mapper>
6.2.1.2. 修改配置文件
在 mappers标签中添加如下内容
6.2.1.3. 编写代码
public class TestDemo {
/**
* 通过id 查询
*/
@Test
public void selectById() throws IOException {
//配置文件路径
String resource = "mybatis.xml";
//通过路径加载配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//获得sqlsessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获得sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 操作数据库
User o = sqlSession.selectOne("huige.findUserById", 10);
//打印结果
System.out.println(o);
//关闭资源
sqlSession.close();
}
}
6.2.1.4. 警告说明
需要导入log4j相关jar包和配置文件
6.2.2. 添加用户
6.2.2.1. 修改映射文件
<!--
添加用户
参数类型 是引用类型
则 #{}中的值 不能胡写 要对应成 实体类中的属性名
-->
<insert id="addUser" parameterType="com.shangma.cn.entity.User">
insert into user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
6.2.2.2. 加载映射文件
已加载过
6.2.2.3. 编写代码
@Test
public void addUser() throws IOException {
String path = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(path);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = factory.openSession();
User user = new User();
user.setUsername("张无忌");
user.setAddress("武当派");
user.setBirthday(new Date());
user.setSex("男");
sqlSession.insert("huige.addUser",user);
//要提交事务 不提交事务 则可能没有真正的持久化硬盘中
sqlSession.commit();
sqlSession.close();
}
6.2.3. 修改用户
6.2.3.1. 修改映射文件
<!-- 修改用户 -->
<update id="updateUser" parameterType="com.shangma.cn.entity.User">
update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
</update>
6.2.3.2. 加载映射文件
已经加载过
6.2.3.3. 编写代码
@Test
public void updateUser() throws IOException {
String path = "mybatis.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(path);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//先查出来 再修改
User o = sqlSession.selectOne("huige.findUserById", 41);
o.setAddress("大草原");
o.setSex("男");
sqlSession.update("huige.updateUser",o);
sqlSession.commit();
sqlSession.close();
}
6.2.3.4. 查看结果
6.2.4. 删除用户
6.2.4.1. 修改映射文件
<!--删除用户-->
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>
6.2.4.2. 加载映射文件
加载过了
6.2.4.3. 编写代码
@Test
public void deleteUser() throws IOException {
String path = "mybatis.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(path);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.delete("huige.deleteUser",10);
//只要不是查询 赠删改 都需要提交事务
sqlSession.commit();
sqlSession.close();
}
6.2.4.4. 查看结果
6.2.5. 模糊查询
6.2.5.1. 修改映射文件
<!--模糊查询 ${}
如果参数为简单类型时,${}里面的参数名称必须为value
-->
<select id="searchUser" parameterType="string" resultType="com.shangma.cn.entity.User">
select * from user where username like '%${value}%'
</select>
6.2.5.2. 加载映射文件
加载过了
6.2.5.3. 编写代码
@Test
public void searchUser() throws IOException {
String path = "mybatis.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(path);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> users = sqlSession.selectList("huige.searchUser", "小");
users.forEach(user -> System.out.println(user));
sqlSession.close();
}
6.2.6. 添加用户返回主键
6.2.6.1. 修改映射文件
<!--
添加用户
参数类型 是引用类型
则 #{}中的值 不能胡写 要对应成 实体类中的属性名
-->
<insert id="addUser" parameterType="com.shangma.cn.entity.User">
<!--
keyProperty 表示主键的属性
order 表示返回的主键是在添加前 还是添加后 取值after before
resultType 表示返回值类型
last_insert_id() 是mysql中的函数 返回的是最后一次添加的id
-->
<selectKey keyProperty="id" order="AFTER" resultType="int">
select last_insert_id()
</selectKey>
insert into user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
7. 面临的问题
- 问题一:代码中 存在大量的重复代码
- 问题二:我们mybatis是持久层框架属于dao的部分
生命周期介绍
8. 简单的封装
/**
* 对重复的代码进行简单封装
*/
public class TestDemo2 {
SqlSessionFactory sqlSessionFactory;
SqlSession sqlSession;
InputStream inputStream;
@Before
public void init() throws IOException {
System.out.println("init方法打印了");
//配置文件路径
String resource = "mybatis.xml";
//通过路径加载配置文件
inputStream = Resources.getResourceAsStream(resource);
//获得sqlsessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
}
/**
* 通过id 查询
*/
@Test
public void selectById() throws IOException {
//获得sqlSession
// 操作数据库
User o = sqlSession.selectOne("huige.findUserById", 30);
//打印结果
System.out.println(o);
}
@Test
public void addUser() throws IOException {
User user = new User();
user.setUsername("张三");
user.setAddress("北京");
user.setBirthday(new Date());
user.setSex("男");
sqlSession.insert("huige.addUser",user);
}
@Test
public void updateUser() throws IOException {
//先查出来 再修改
User o = sqlSession.selectOne("huige.findUserById", 30);
o.setAddress("上海");
o.setSex("女");
sqlSession.update("huige.updateUser",o);
}
@Test
public void deleteUser() throws IOException {
sqlSession.delete("huige.deleteUser",10);
}
@Test
public void searchUser() throws IOException {
List<User> users = sqlSession.selectList("huige.searchUser", "小");
users.forEach(user -> System.out.println(user));
}
@After
public void release(){
System.out.println("release方法打印了");
sqlSession.commit();
sqlSession.close();
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
其实每次操作数据库时 先执行init 操作完数据库之后 又执行release 也就是说 每次都会初始化一个sqlSession 如果全局就一个sqlsession时 这种方式 就不可以了
9. myBatis的dao开发
9.1. 常见dao接口
public interface UserDao {
void add(User user);
}
9.2. 创建实现类
public class UserDaoImpl implements UserDao {
private SqlSession sqlSession;
public UserDaoImpl(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public void add(User user) {
sqlSession.insert("aaa.insertUser",user);
}
}
9.3. 编写测试类
public class Test02 {
private SqlSession sqlSession;
private InputStream inputStream;
private SqlSessionFactory sqlSessionFactory;
@Before/*最先执行*/
public void before() {
/*配置文件路径*/
String resource = "mybatis-config.xml";
/*通过路径加载配置文件*/
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
/*获得sqlSessionFactory*/
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
/*获得sqlSession*/
sqlSession = sqlSessionFactory.openSession();
}
@After/*最后执行*/
public void after() throws IOException {
/*只要不是查询,赠删改都需要提交事务*/
sqlSession.commit();
/*关闭流*/
sqlSession.close();
inputStream.close();
}
@Test//根据id查找
public void fun() throws IOException {
//操作数据库
User o = sqlSession.selectOne("aaa.findUserById", 1);
//打印结果
System.out.println("查询结果:" + o.toString());
}
@Test//查找
public void fun2() throws IOException {
// 操作数据库
List<User> objects = sqlSession.selectList("aaa.findUser");
objects.forEach(item -> {
System.out.println("Lambda表达式: " + item);
});
}
@Test//删除
public void fun3() throws IOException {
// 操作数据库
sqlSession.delete("aaa.deleteUser", 1);
}
@Test//添加
public void fun4() throws IOException {
User user = new User();
UserDao userDao = new UserDaoImpl(sqlSession);
user.setUsername("马保国");
user.setAddress("混元太极门");
user.setBirthday(new Date());
user.setSex("男");
// 操作数据库
//sqlSession.insert("aaa.insertUser", user);
userDao.add(user);
System.out.println(user);
}
}
10.myBatis的Mapper开发(重点)
Mapper代理的方式 主要是解决 不写dao实现类 程序员只需要编写Mapper接口就可以了 但满足规范时 MyBatis 会自动生成Mapper的代理实现类 操作
10.1. Mapper代理书写规范
- Mapper接口的完整路径 要和Mapper中的namespace一致
- Mapper接口中的方法 要和映射文件中select update insert delete标签中的 id值一致
- Mapper接口中方法的参数只能有一个 并且类型要和映射文件中select update insert delete标签中的parameterType的类型一致 、
- Mapper接口中返回值要和要和映射文件中select update insert delete标签中的resultType或者resultMap的类型一致
10.2. 编写Mapper接口
public interface UserMapper {
//查询所有
List<User> selectAll();
//根据id查询
User selectById(Integer id);
//添加用户
void saveUser(User user);
}
10.3. 编写配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shangma.cn.mapper.UserMapper">
<!--查询所有 -->
<select id="selectAll" resultType="com.shangma.cn.entity.User">
select * from user;
</select>
<!--根据id查询-->
<select id="selectById" parameterType="java.lang.Integer" resultType="com.shangma.cn.entity.User">
select * from user where id = #{id}
</select>
<!--添加用户-->
<insert id="saveUser" parameterType="com.shangma.cn.entity.User">
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
select last_insert_id();
</selectKey>
insert into user(username,sex,birthday,address) values(#{username},#{sex},#{birthday},#{address})
</insert>
</mapper>
10.4. 编写测试类
public class MapperTest {
@Test
public void testMapper() throws IOException {
String path = "mybatis.xml";
InputStream in = Resources.getResourceAsStream(path);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sqlSessionFactory.openSession(true);//设置为true时 自动提交事务
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//查询所有
//List<User> users = mapper.selectAll();
//users.forEach(user -> System.out.println(user));
//根据id查询
//System.out.println(mapper.selectById(30));
// 添加
User user = new User();
user.setUsername("腾格尔");
user.setBirthday(new Date());
user.setSex("男");
mapper.saveUser(user);
sqlSession.close();
in.close();
}
}
11.全局配置文件讲解
11.0. 配置详情
11.1. 名字的规范
全局配置文件的名字 可以随意写 想怎么写 怎么写 但是一般的情况下 会叫如下几个名字
- sqlMapperConfig.xml
- mybatis-config.xml
11.2. 数据源问题
11.3. 连接信息抽出
11.3.1. 编写properties文件
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8
jdbc.username=root
jdbc.password=root
11.3.2. 读取配置文件
11.3.3. 加载配置文件
11.4. 别名问题(了解)
我们的 参数类型 和 返回值类型 如果是引用类型 可以起别名
11.4.1. 起别名
-
方式一:
<typeAliases> <typeAlias type="com.shangma.cn.entity.User" alias="user"/> </typeAliases>
-
方式二:
<typeAliases> <!--指定包名时 别名就是包中对应类的类名 不区分大小写--> <package name="com.shangma.cn.entity"/> </typeAliases>
11.4.2. 使用别名
11.5. mapper的属性值
11.5.1. resource属性
这个属性的值 是类路径下的资源
<mappers>
<!--mapper的写法 resource 表示从src下(类路径下 开始找 使用文件夹的形式 而不是.)-->
<mapper resource="com/shangma/cn/mapper/user.xml"/>
</mappers>
11.5.2. url属性
这个属性的值 表示完整路径 带盘符 带协议
<mappers>
<mapper url="file:///F:\mybaits01\src\com\shangma\cn\mapper\user.xml"/>
</mappers>
11.5.3. class属性
这个属性值 表示 mapper接口的完整路径
要求:接口名称要和mapper文件名一致 并且在同一个目录下
11.5.4. 开发中的写法
直接写包名 这样的话 如果mapper映射文件比较多的时候 可以统一设置
不需要再一个一个单独设置了
要求:接口名称要和mapper文件名一致 并且在同一个目录下
<mappers>
<package name="com.shangma.cn.mapper"/>
</mappers>
12. 映射文件
12.1. parameterType
- 表示参数类型
- 取值
- 基本数据类型
- map
- 自定义类型
- 包装类
12.1.1. 基本数据类型
<!--
基本数据类型
parameterType="java.lang.Integer" 可以写包装类 类的完整路径
parameterType="int" 可以写别名
-->
<select id="selectById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
12.1.2. 实体类
<!--
如果是实体类
parameterType="com.shangma.cn.entity.User" 写类的完整路径
如果起了别名
例如
<typeAliases>
<package name="com.shangma.cn.entity"/>
</typeAliases>
则可以写别名
parameterType="user"
则使用时 #{}是这个实体类中的属性名
-->
<insert id="saveUser" parameterType="user">
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
select last_insert_id();
</selectKey>
insert into user(username,sex,birthday,address) values(#{username},#{sex},#{birthday},#{address})
</insert>
12.1.3. 包装类
-
创建包装类
public class UserExt { private User user; private String like="张"; public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String getLike() { return like; } public void setLike(String like) { this.like = like; } @Override public String toString() { return "UserExt{" + "user=" + user + ", like='" + like + '\'' + '}'; } }
-
在userMapper中添加内容
// 模糊查询 List<User> searchUser(UserExt userExt);
-
编写映射文件
<select id="searchUser" parameterType="com.shangma.cn.entity.UserExt" resultType="com.shangma.cn.entity.User"> select * from user where sex=#{user.sex} and username like '%${like}%' </select>
-
测试
@Test public void testMapper() throws IOException { String path = "mybaits-config.xml"; InputStream in = Resources.getResourceAsStream(path); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sqlSessionFactory.openSession(true);//设置为true时 自动提交事务 //getMapper表示底层使用了动态代理 UserMapper mapper = sqlSession.getMapper(UserMapper.class); UserExt userExt = new UserExt(); User user = new User(); user.setSex("男"); userExt.setUser(user); List<User> users = mapper.searchUser(userExt); users.forEach(user1 -> System.out.println(user1)); sqlSession.close(); in.close(); }
12.2. resultType
返回值类型
注意点:返回单个实体类和实体类集合时 mapper映射文件中的resultType的类型是一样的,
问题:这种单独resultType的方式 依赖于 属性名和表中的列名一致 所以数据会封装到实体类中
###12.3. resultMap
如果表中的字段和实体类中的属性名不一致时 我们需要使用 resultMap
-
第一步 修改表中字段
-
第二步:使用resultmap 设置属性和别名的对应关系
<!-- id属性 表示resultmap的唯一标识 不能重复 唯一的 随意写 type 表示返回值的类型 或者可以理解为 指定的是哪个类 result 表示 实体类属性和表的别名的对应关系 column:表示表的列明 property: 表示实体类属性名 如果列是id的话 可以使用id标签 --> <resultMap id="huige" type="user"> <id column="id_" property="id"/> <!--<result column="id_" property="id"/>--> <result column="address_" property="address"/> </resultMap>
-
第三步 : 使用resultMap
13.sql 代码片
13.1. 问题演示
13.2. if的写法
<select id="searchUser" parameterType="com.shangma.cn.entity.UserExt" resultMap="huige">
select * from user where 1=1
<if test="user!=null">
and sex=#{user.sex}
</if>
<if test="like!=null and like != '' ">
and username like '%${lile}%'
</if>
</select>
13.3. where的写法
<select id="searchUser" parameterType="com.shangma.cn.entity.UserExt" resultMap="huige">
select * from user
<where>
<if test="user!=null">
and sex=#{user.sex}
</if>
<if test="like!=null and like != '' ">
and username like '%${lile}%'
</if>
</where>
</select>
13.4. 其他写法
13.5. foreach 标签使用
-
使用场景 : 查询多个id的用户信息
select * from user where id in(x,x,x)
-
mapper接口中添加方法
List<User> selectByIds(UserExt userExt);
-
编写mapper文件
<!-- collection 要遍历的集合 open 表示开始的内容 close 表示结束的内容 separator 表示分割的内容 item 表示每次遍历出来的内容 --> <select id="selectByIds" parameterType="com.shangma.cn.entity.UserExt" resultMap="huige"> select * from user where id_ in <foreach collection="ids" open="(" close=")" separator="," item="id"> #{id} </foreach> </select>
-
测试
@Test public void testMapper() throws IOException { String path = "mybaits-config.xml"; InputStream in = Resources.getResourceAsStream(path); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sqlSessionFactory.openSession(true);//设置为true时 自动提交事务 UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<Integer> list = new ArrayList<>(); list.add(28); list.add(29); list.add(30); list.add(31); UserExt userExt = new UserExt(); userExt.setIds(list); List<User> users = mapper.selectByIds(userExt); users.forEach(user1 -> System.out.println(user1)); sqlSession.close(); in.close(); }
14.mybatis中的多表关系
实际开发中 表和表之间存在一定的关系 这种关系不一定需要数据库中的约束 例如外键 表的关系 有如下几种
- 一对一
- 一对多
- 多对多
14.1. 环境搭建
<?xml version="1.0" encoding="UTF-8"?>
<!--mybatis全局配置文件的约束-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--配置信息 -->
<configuration>
<!--引入资源配置外部文件-->
<properties resource="db.properties"/>
<!--开启驼峰-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--mybatis打印日志信息-->
<!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
</settings>
<!--使用别名 包扫描 直接使用实体类名即可-->
<typeAliases>
<package name="com.sm.entity"/>
</typeAliases>
<!--配置环境-->
<environments default="development">
<environment id="development">
<!--jdbc的事务管理 交给mybatis管理 -->
<transactionManager type="JDBC"/>
<!--数据源 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--使用包查扫描 必须和Mapper在一个包下xml名字必须和Mapper名字相同 同包同目录-->
<package name="com.sm.mapper"/>
</mappers>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!--Mapper映射-->
<!--使用Mapper代理进行CRUD-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sm.mapper.UserMapper">
<resultMap id="selectUserAndCartMap" type="User">
<result column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<association javaType="Cart" property="cart">
<result column="cartId" property="cartId"/>
<result column="userId" property="userId"/>
<result column="totalnum" property="totalnum"/>
<result column="totalmoney" property="totalmoney"/>
</association>
</resultMap>
<select id="selectUserAndCart" resultMap="selectUserAndCartMap">
select * from user a,cart b where a.id=b.userId
</select>
<!--mybatis 使用了别名中包扫描实体类 故可以直接使用当做别名-->
<!-- id: 接口中的方法-->
<!-- type: 表示返回值的类型,指定的是哪个类-->
<resultMap id="selectUserGoods" type="User">
<result column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!--[一对一]-->
<!-- javaType: 表示这个关联对象属性的java类型-->
<!-- property: 表示一对一关联对象在另一个对象中的属性名-->
<association javaType="Cart" property="cart">
<result column="cartId" property="cartId"/>
<result column="userId" property="userId"/>
<result column="totalnum" property="totalnum"/>
<result column="totalmoney" property="totalmoney"/>
<!--[一对多]-->
<!-- ofType: 表示List<T>的泛型-->
<!-- property: 表示多的一个方法List<T> 属性名-->
<collection ofType="com.sm.entity.Cartitem" property="cartitems">
<result column="cartItemId" property="cartItemId"/>
<result column="cartId" property="cartId"/>
<result column="pid" property="pid"/>
<result column="pnum" property="pnum"/>
<result column="pmoney" property="pmoney"/>
<association property="good" javaType="Good">
<result column="pid" property="pid"/>
<result column="pname" property="pname"/>
<result column="price" property="price"/>
<result column="pimg" property="pimg"/>
<result column="pdesc" property="pdesc"/>
</association>
</collection>
</association>
</resultMap>
<!--[sql]-->
<!-- id: 接口中执行的方法-->
<!-- resultMap: resultMap的id-->
<select id="selectUserGoods" resultMap="selectUserGoods">
SELECT * FROM user a INNER JOIN cart b ON a.id=b.userId INNER JOIN cartitem c ON b.cartId=c.cartId INNER JOIN good d ON c.pid=d.pid
</select>
</mapper>
14.1.1. 建表建库
CREATE TABLE `cart` (
`cartId` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`userId` int(11) NULL DEFAULT NULL,
`totalnum` int(11) NULL DEFAULT NULL,
`totalmoney` double(255, 0) NULL DEFAULT NULL,
PRIMARY KEY (`cartId`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
INSERT INTO `cart` VALUES ('4275a23c41694cc0ae54f2a8d97b024b', 5, 1, 129);
INSERT INTO `cart` VALUES ('9e96b8b3a71c4700bfc0a4b164a3887e', 4, 3, 9664);
CREATE TABLE `cartitem` (
`cartItemId` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`cartId` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`pid` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`pnum` int(11) NULL DEFAULT NULL,
`pmoney` double(255, 0) NULL DEFAULT NULL,
PRIMARY KEY (`cartItemId`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
INSERT INTO `cartitem` VALUES ('5c8334e949eb471ca32d8d2c2d5db9ed', '9e96b8b3a71c4700bfc0a4b164a3887e', '1d062d5fcaf147468aa325af6715df18', 1, 5666);
INSERT INTO `cartitem` VALUES ('b6a42a8be7584c269399d67b3d2b7452', '9e96b8b3a71c4700bfc0a4b164a3887e', '082d077043e74f08b5c8e82c33990d05', 2, 3998);
INSERT INTO `cartitem` VALUES ('ff81edf0755d4f5e8e5475745185de2a', '4275a23c41694cc0ae54f2a8d97b024b', '4e6bc5c119a848da8678e6a4d3ec1057', 1, 129);
CREATE TABLE `good` (
`pid` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`pname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`price` double(10, 2) NULL DEFAULT NULL,
`pimg` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`pdesc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
INSERT INTO `good` VALUES ('082d077043e74f08b5c8e82c33990d05', '手机', 1999.00, '/images/shouji.jpg', '手机很快');
INSERT INTO `good` VALUES ('1b64510f93104a7886c334351e465b80', '保暖内衣', 56.00, '/images/baonuan.jpg', '很暖和');
INSERT INTO `good` VALUES ('1d062d5fcaf147468aa325af6715df18', '电视', 5666.00, '/images/dianshi.jpg', '1080P');
INSERT INTO `good` VALUES ('224c796995744ae5a22f6d8d72eefc45', '电脑', 8888.00, '/images/diannao.jpg', '128G内存');
INSERT INTO `good` VALUES ('46ea4fabaf8741a3a455e30c83607086', '卫衣', 99.00, '/images/weiyi.jpg', '裹得很严实');
INSERT INTO `good` VALUES ('4e6bc5c119a848da8678e6a4d3ec1057', '吹风机', 129.00, '/images/chuifengji.jpg', '风很大');
14.1.2. 表关系介绍
- 一个用户只有一个购物车 -> 一对一
- 一个购物车有多个购物车项 -> 一对多
- 一个购物车项 对应一个商品 -> 一对一
- 一个用户可以勾选多个商品 一个商品也可以被多个用户勾选 -> 多对多
14.1.3. 编写实体类
-
购物车
public class Cart { private String cartId;//购物车id private Integer userId; //用户id private int totalnum; //总数量 private double totalmoney;//总钱数 public String getCartId() { return cartId; } public void setCartId(String cartId) { this.cartId = cartId; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public int getTotalnum() { return totalnum; } public void setTotalnum(int totalnum) { this.totalnum = totalnum; } public double getTotalmoney() { return totalmoney; } public void setTotalmoney(double totalmoney) { this.totalmoney = totalmoney; } @Override public String toString() { return "Cart{" + "cartId='" + cartId + '\'' + ", userId=" + userId + ", totalnum=" + totalnum + ", totalmoney=" + totalmoney + '}'; } }
-
购物车中的每一条
public class CartItem { private String cartItemId; //购物车项id private String cartId;//购物车id private String pid; //商品id private int pnum; //商品数量 private double pmoney; // 小计 public String getCartItemId() { return cartItemId; } public void setCartItemId(String cartItemId) { this.cartItemId = cartItemId; } public String getCartId() { return cartId; } public void setCartId(String cartId) { this.cartId = cartId; } public String getPid() { return pid; } public void setPid(String pid) { this.pid = pid; } public int getPnum() { return pnum; } public void setPnum(int pnum) { this.pnum = pnum; } public double getPmoney() { return pmoney; } public void setPmoney(double pmoney) { this.pmoney = pmoney; } @Override public String toString() { return "CartItem{" + "cartItemId='" + cartItemId + '\'' + ", cartId='" + cartId + '\'' + ", pid='" + pid + '\'' + ", pnum=" + pnum + ", pmoney=" + pmoney + '}'; } }
-
商品
public class Good { private String pid; //商品id private String pname; //商品名称 private double price; //商品价格 private String pimg; //商品图 private String pdesc;//商品描述 public String getPid() { return pid; } public void setPid(String pid) { this.pid = pid; } public String getPname() { return pname; } public void setPname(String pname) { this.pname = pname; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public String getPimg() { return pimg; } public void setPimg(String pimg) { this.pimg = pimg; } public String getPdesc() { return pdesc; } public void setPdesc(String pdesc) { this.pdesc = pdesc; } @Override public String toString() { return "Good{" + "pid='" + pid + '\'' + ", pname='" + pname + '\'' + ", price=" + price + ", pimg='" + pimg + '\'' + ", pdesc='" + pdesc + '\'' + '}'; } }
14.2. 一对一的写法
- 需求 查询用户以及他的购物车信息
14.2.1. 修改User实体类
14.2.2. 编写Mapper
14.2.3. 编写映射文件
14.2.4. 测试以及结果
14.3. 一对多的写法
- 需求: 查询购物车以及购物车中的项
14.3.1. 修改购物车实体类
14.3.2. 编写Mapper
14.3.3. 编写映射文件
14.3.4. 测试结果
14.4. 多对多的写法
14.4.1. 修改cartItem实体类
14.4.2. 编写Mapper
14.4.3. 编写映射文件
15. mybatis的延迟加载
延迟加载 又叫懒加载 表示在关联查询中 当使用关联的数据时 再去加载 没有使用 则先不加载 提交数据库的性能 和效率
resultMap中的association和collection标签具有延迟加载的功能。
拿购物车和购物车详情为例,但是注意 之前的写法是内连接的方式 一下子全部查出来了 我们还要改造我们的映射文件
15.1. setting配置
15.2. 编写cartItemMapper
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6KIr2vsB-1615510723756)(images/QQ图片20200204181321.png)]
15.3. 编写映射文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cuMf7Xvu-1615510723756)(images/QQ图片20200204181418.png)]
15.4. 修改cart的映射文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JSHXvFbm-1615510723757)(images/QQ图片20200204181758.png)]
15.5. 测试延迟加载
略
16. mybatis的缓存
16.1. 缓存的了解
所有的缓存 都是一个思想 数据存在一个地方 (例如redis中)
- 查询数据 获得结果 此时存缓存的地方还没有数据
- 把查询出的结果 放到存缓存的地方 让有数据
- 再查询时 先判断 这个地方有没有数据 如果有 直接拿出来 则不会再查数据库
- 当修改时 清空缓存
16.2. Mybatis的一级缓存
mybatis的一级缓存 存在内存中 是sqlSession级别的缓存 缓存的结构是个Map集合
map的key 是sql语句 条件 等等信息组成的唯一值
map的value 就是查询出来的结果
mybatis的一级缓存 默认是开启的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RQ6iY7Kd-1615510723758)(./images/QQ图片20200204183028.png)]
16.2.1. 一级缓存的演示
编写一个查询所有的mapper和映射文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oSNl3Mvp-1615510723758)(images/QQ图片20200204184609.png)]
16.3. myBatis的二级缓存
mybatis的二级缓存 是 sqlsessionFactory级别的缓存 或者说是Mapper或者说 namespace级别的缓存
当使用查询时,查询结果会存入对应的namespace中.
当所属namespace使用增删改时,会清空该namespace中的缓存
二级缓存可能会存入内存,也可能会存入硬盘
由于二级缓存可能会存入硬盘,所以需要将对应需要缓存的实体类进行序列化(implements Serializable)。
myBatis的二级缓存 高版本默认是开启的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0S9DITO5-1615510723759)(images/QQ图片20200204185939.png)]
16.3.1. 演示二级缓存
-
修改实体类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-91rrzaor-1615510723760)(images/QQ图片20200204190728.png)]
-
修改映射文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Shxz4f4F-1615510723761)(images/QQ图片20200204190830.png)]
-
测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ieHoa18n-1615510723761)(images/QQ图片20200204190918.png)]
16.3.2. 局部不使用缓存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a4x84VXV-1615510723762)(images/QQ图片20200204191230.png)]
16.3.3. 刷新二级缓存
默认情况下
如果是select语句,那么flushCache是false。
如果是insert、update、delete语句,那么flushCache是true。
如果查询语句设置成true,那么每次查询都是去数据库查询,即意味着该查询的二级缓存失效。
如果查询语句设置成false,即使用二级缓存,那么如果在数据库中修改了数据,
而缓存数据还是原来的,这个时候就会出现脏读。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L9xhyV61-1615510723763)(images/QQ图片20200204191607.png)]
17. Mybatis的注解开发 (了解 )
使用注解开发 可以不用写 映射文件
17.1. 普通的crud
public interface UserMapper {
/**
* 查询所有
*
* @Select 注解 表示查询 代替映射文件中的select标签
*/
@Select("select * from user")
public List<User> selectAll();
/**
* 根据id查询
* @return
*/
@Select("select * from user where id = #{userId}")
public User selectUserById(Integer userId);
/**
* 添加用户
* @return
*/
@Insert("insert into user(username,sex,birthday,address) values(#{username},#{sex},#{birthday},#{address})")
public void addUser(User user);
/**
* 修改用户
* @return
*/
@Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}")
public void updateUser(User user);
/**
* 删除用户
* @param userId
*/
@Delete("delete from user where id = #{userId}")
public void deleteUserById(Integer userId);
}
17.2. 属性名和列名不一致的情况
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YHz6QvMI-1615510723763)(images/QQ图片20200204194032.png)]
18. 探索驼峰命名的规则
18.1. 面临的问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fX6TjRYO-1615510723764)(images/QQ图片20200204195823.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q6A1hx4C-1615510723764)(images/QQ图片20200204200009.png)]
18.2. 开启驼峰命名
虽然我们可以使用resultMap或者result注解进行匹配 但是这样做复杂 实际开发中 遵循驼峰命名非常必要
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3FSMQ3tC-1615510723765)(images/QQ图片20200204200454.png)]
- 测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FCKRkU62-1615510723765)(./images/QQ图片20200204200829.png)]
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/192846.html