这两个链接 基本上把本目录的所有知识点都包含进去了
mybatis高级查询具体操作
mybatis增删改查基本操作
1、加载驱动问题:
每次执行都加载驱动
驱动名称,硬编码到java代码中,如果需要修改驱动。需要修改java文件
解决方案:将驱动名称放入到外部的配置文件
2、数据库的连接信息,硬编码到java代码中,解决方案:外部配置文件
3、设置参数的问题:
参数下标硬编码了。需要人为的去判断参数的位置。
4、遍历结果集:需要人工的判断字段名,以及个位置参数类型,不方便
是否可以:能够将结果集直接映射到一个pojo对象中
5、频繁的创建连接,关闭连接。导致资源浪费,影响性能,解决:连接池。
你会五大框架吗?
SSH spring struts2(webcontroller—[servlet]) hibernate (dao=jdbc—db)
SSM spring springmvc(webcontroller—[servlet] service[m] ) mybatis(dao=jdbc—db)
Cn.yanqi.bean domain entity pojo 实体类
在mybatis的包里面可以看到,ibatis的字样。
每一个框架都有一个核心配置文件,这个核心文件你必须会,不会这个框架就用不了
创建数据表 tb_user
CREATE TABLE `tb_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(20) DEFAULT NULL,
`sex` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
在工程的根目录src下面创建一个mybatis-config.xml 。这个名字不是固定的,可以随便起,
但也不能太随便,通常都叫mybatis-config.xml
注意:mybatis-config.xml 引入外部的jdbc.properties文件
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200311065412420.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTE1MTk2MA==,size_16,color_FFFFFF,t_70)
1、创建一个局部的mapper.xml(名字也可以随便写,但是不能太随便,位置可以放在项目里的任何位置,只需要你能加载到对应的mpper.xml即可)
通常我们就叫标签为一个statement对象或者叫mapped statement对象
可以通过官方文档里找路径如下:
打开index.html
其中Mybatis-config.XML文件的位置不是固定的,只要能找到xml文件就行,可以随便放
2、在全局的mybatis-config.xml 中去添加mapper.xml的配置
3、书写java代码,调用指定的statement,并且传递参数信息。获取返回值
注意:需要在mybatis-config.xml 中使用mapper 去引入外部的mapper.xml
放在src下面
public interface UserDao {
/**
* 根据id查询用户信息
* @param id
* @return
*/
public User queryUserById(int id);
/**
* 查询所有用户信息
* @return
*/
public List<User> queryAllUser();
/**
* 根据id删除用户信息
* @param id
*/
public void deleteUserById(int id);
/**
* 添加用户信息
* @param user
*/
public void addUser(User user);
/**
* 修改用户信息
* @param user
*/
public void updateUser(User user);
}
public class UserDaoImpl implements UserDao{
//需要sqlSession的来操作
private SqlSession sqlSession;
//临时解决方案,利用构造方法传入
public UserDaoImpl(SqlSession sqlSession){
this.sqlSession = sqlSession;
}
//根据id来查询用户
@Override
public User queryUserById(int id) {
return sqlSession.selectOne("user.queryUserById", id);
}
//查询所有
@Override
public List<User> queryAllUser() {
return sqlSession.selectList("user.queryAllUser");
}
//删除用户
@Override
public void deleteUserById(int id) {
sqlSession.delete("user.deleteUserById", id);
}
//添加用户
@Override
public void addUser(User user) {
sqlSession.insert("user.addUser", user);
}
//修改用户
@Override
public void updateUser(User user) {
sqlSession.update("user.updateUser", user);
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="user">
<!-- 根据id来查询 -->
<select id="queryUserById" resultType="cn.yanqi.mybaits.pojo.User">
select * from t_user where id = #{id}
</select>
<!-- 查询所有 -->
<select id="queryAllUser" resultType="cn.yanqi.mybaits.pojo.User">
select * from t_user
</select>
<!-- 根据id来删除用户 没有结果集的映射的java对象-->
<delete id="deleteUserById">
delete from t_user where id = #{id}
</delete>
<!-- 添加用户 -->
<insert id="addUser">
INSERT INTO t_user
(id,
name,
sex)
VALUES(
NULL,
#{name},
#{sex}
)
</insert>
<!-- 修改用户 #{name} pojo类属性一样-->
<update id="updateUser">
UPDATE t_user
SET
name = #{name},
sex = #{sex}
WHERE
id = #{id}
</update>
</mapper>
在mybatis-config.xml里面引入UserMapper.xml
public class UserDaoTest {
//new 一个userDao对象
private UserDao userDao;
private SqlSession sqlSession;//定义成全局的对象
@Before//执行test方法之前执行
public void setUp() throws Exception {
String resource ="mybatis-config.xml";
//读取xml配置文件
InputStream is = Resources.getResourceAsStream(resource);
//构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//打开SqlSession,得到sqlSession可以进行curd的操作
sqlSession = sqlSessionFactory.openSession();
//userDao = new UserDaoImpl(sqlSession);
//获取mapper的动态代理实现类
userDao = sqlSession.getMapper(UserDao.class);
}
//根据id查询
@Test
public void testQueryUserById() {
//调用userDao方法
User user = userDao.queryUserById(1);
System.out.println(user);
}
//查询所有
@Test
public void testQueryAllUser() {
//调用userDao方法
List<User> users = userDao.queryAllUser();
//遍历输出
for(User user : users){
System.out.println(user);
}
}
//根据id来删除
@Test
public void testDeleteUserById() {
userDao.deleteUserById(6);
sqlSession.commit();//必须开启事务
}
//添加user
@Test
public void testAddUser() {
User user = new User();
user.setName("yanqi");
user.setSex("boy");
userDao.addUser(user);
sqlSession.commit();//不开启事务是写入不到数据库里去的
}
//修改用户
@Test
public void testUpdateUser() {
// 1、先去查询
User user = userDao.queryUserById(1);
user.setName("yanqi");
userDao.updateUser(user);
// 提交事务
sqlSession.commit();
}
}
查询数据的时候,查不到userName的信息,原因:数据库的字段名是user_name
POJO中的属性名字是userName
两端不一致,造成mybatis无法填充对应的字段信息。修改方法:在sql语句中使用别名
解决方案1:在sql语句中使用别名(麻烦)
Select * , user_name as username from t_user;
解决方案2: 参考后面的resultMap –mapper具体的配置的时候
解决方案3:参考驼峰匹配 — mybatis-config.xml 的时候
在mybatis中dao层的接口的名字不在使用***Dao 而是修改成***Mapper
例如:把UserDao,修改成UserMapper。 UserDao(Mapper)
接口->实现类->mapper.xml
思考:能否只写UserDao接口,不书写实现类UserDaoImp,只编写Mapper.xml即可
使用动态代理的目标是可以不用书写实现类,只需要书写接口和mapper.xml即可完成crud的操作
如果不想写实现类(UserDaoImpl ) ,只写接口UserDao ---- 就可以使用动态代理
因为在dao(mapper)的实现类中对sqlsession的使用方式很类似。mybatis提供了接口的动态代理
mapper.xml 根标签的 namespace 属性
如果希望使用mybatis通过的动态代理的接口,就需要namespace 中的值,和需要对应的Mapper(dao)接口的全路径一直
错误原因:mapper接口的全路径和对应mapper.xml的名称空间不一致造成。
开启驼峰匹配:从经典数据库的命名规则user_name,到经典java命名规则的映射userName
java命名规则:驼峰书写, 大小写区分两个单词的界限。举例: userName;
数据库经典命名规则:两个单词之间,使用下划线分割。举例:user_name
开启驼峰匹配:相当于去掉数据库名字中的下划线,然后在与java中的属性名进行对应。
数据库中的user_name 和java属性中的 userName 是一样的
在mybatis-config.xml中:
给类起一个别名
缺点:需要为每一个类都去定义一个类型别名。书写麻烦。
作用:将mapper.xml 文件配置到mybatis-config.xml的环境中。
这里所谓的mapper接口路径。实际上就是dao的接口路径。在mybatis中,通常把dao的包叫做mapper
类名,也叫做mapper
1、定义一个接口
2、在接口所在的包中定义mapper.xml
3、在mybatis-config.xml 中通过class路径,引入mapper。要求mapper.xml 中的名称空间是类的接口的全路径
问题:
1、mapper.xml 和 java文件没有分离。spring整合之后解决。
2、需要一个一个的去加载mapper
扫描的是包
缺点:
1、如果包的路径有很多?2、mapper.xml和mapper.java没有分离。
2、在spring整合的时候可以解决这个问题
2settings:开启驼峰匹配。
从数据库的经典命名规则到java经典命名规则的映射; 把数据库中的下划线去顶在和java中的内容进行映射。
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
3、typealiases:类型别名。 为一些java对象,去起一个简写的名字,减少mapper.xml中的 代码书写量。
<typeAliases>
<package name="cn.yanqi.mybatis.pojo"/>
</typeAliases>
4、环境:数据库的连接信息
5、mappers:关联外部的mapper.xml文件的
把外部的mapper.xml加载到mybatis中
扫描包的方式:
<mappers>
<!--
扫描指定包下面的所有的接口
要求:
1、要求mapper.xml文件的名字和mapper接口的名字,一致
2、要求mapper.xml文件 和 mapper接口类在一个目录下
-->
<package name="cn.yanqi.mybatis.mapper"/>
<mapper resource="cn/yanqi/mybatis/resources/mapper.xml"/>
<mapper class="cn.yanqi.mybatis.mapper.UserMapper"/>
</mappers>
select – 书写查询sql语句
id属性:当前名称空间下的statement的唯一标识。必须。要求id和mapper接口中的方法的名字一致。
Select 标签中 resultType:将结果集映射为java的对象类型必须要有(和 resultMap 二选一)
parameterType:传入参数类型。可以省略,会自判断传入的是什么类型
insert 的几个属性说明:
id属性:当前名称空间下的statement的唯一标识(必须属性);
parameterType:传入的参数类型,可以省略。
标签内部:具体的sql语句。insert语句
使用#{} 去替换一个变量。
怎么知道我们添加时是否添加成功怎么办?
UserMapper.java 接口中把返回类型直接写成Integer返回类型,其他的不用动
测试类中给一个integer 返回值就行了
添加成功后返回1
可以拿到数据库的自增长的id值
测试:
User.getId();就可以直接拿到自增的id
测试结果:
update 的几个属性说明:
id属性:当前名称空间下的statement的唯一标识(必须属性);
parameterType:传入的参数类型,可以省略。
标签内部:具体的sql语句。
使用#{} 去替换一个变量。
补充:
delete 的几个属性说明:
id属性:当前名称空间下的statement的唯一标识(必须属性);
parameterType:传入的参数类型,可以省略。
标签内部:具体的sql语句。
使用#{} 去替换一个变量。
#{ 只是表示占位可以随便写 } 相当于 ?
#{} ?只能出现where中 ,当作一个变量 ,如要sql语句中要在实体对象中取出数据 ,那么where 后的#{}不能随便写
数 据 : 是 进 行 字 符 串 拼 接 。 如 果 使 用 {} 数据:是进行字符串拼接。 如果使用 数据:是进行字符串拼接。如果使用{} 去取出参数信息,则需要在方法的参数列表上加上一个注释@param 表示参数的名字
#{} 只是表示占位,与参数的名字无关。如果用#{}去传入下例中的一个表名的话,是传不进去的
sql语句动态生成的时候,使用${};
sql语句中某个参数进行占位的时候#{}
mapper.xml中的内容:
新写一个接口的方法
定义传入参数的类型:
如果 没 有 指 定 参 数 名 , 使 用 {} 没有指定参数名 ,使用 没有指定参数名,使用{value} 表示传递过来的参数。
如果接口中书写的@Param(“TableName”) 就输入 ${TableName}
可以在方法的参数列表列表上 @param(“参数名”)
1、如果使用$去传递参数的时候,使用@Param 把参数指定一个名字
2、如果传入多个参数的时候,使用@Param,一定需要使用为每一个参数都去指定名字
如果接口代码这样书写:
mapper.xml
传递多个参数的时候出现如下错误:
对于传入多个参数的时候,#{} 需要使用参数名的方式去获取数据。
java代码:
解决方案1 不推存使用:
在mapper.xml中使用 0,1这样的序号去,0表示第一个参数,1表示第二个参数。(从0开始数)
通过@param(“ 数据名”) 取出 # { 数据名 }
${} statement对象
#{} Preparedstatement对象
${} 方式:
1、KaTeX parse error: Expected 'EOF', got '#' at position 7: 字符串拼接,#̲ 参数站位相当于jdbc中的?…不能够防止sql注入,#可以防止sql注入的
3、KaTeX parse error: Expected 'EOF', got '#' at position 17: …以替换sql语句任何一个内容,#̲只能替换参数 4、如果操作字符串,需要在sql中使用单引号。 #不需要(不需要判断数据类型,会自动转换)($要考虑参类型 ,#不用考虑参数类型)
简单来说#{} 解析的是占位符?可以防止SQL注入, 比如打印出来的语句 select * from table where id= ? 然而${} 作为字符串拼接来用,则是不能防止SQL注入打印出来的语句 select * from table where id=2 实实在在的参数 (sql注入:通过字符串拼接达到串改sql语句的目地)
8.5.1: $不能够防止sql注入,#可以防止sql注入的
以下案例演示的要判断数据类型
案例:根据姓名进行模糊查询
定义一个接口的方法
【补充】
$可以代替所有#
如果传入的数据,不是sql中的字段的时候,就不能够使用#.
通常使用#。
选择获取参数的时候,首要选择的# 的方式(1、可以防止sql注入,2、可以不用考虑数据类型,简化书写,3、sql是参数的话的sql,预编译的sql,速度会块一些)
当#用不了的时候,去选择 例 如 , s q l 需 要 改 变 是 表 名 的 情 况 , 就 可 使 用 例如 ,sql需要改变是表名的情况,就可使用 例如,sql需要改变是表名的情况,就可使用的方式。
总结:能用# 就不选择$
作用:把重复的sql语句抽出来来放到sql标签中,然后通来引入
用法1
在一个mapper.xml 中使用 去定义sql片段,然后在需要的位置使用 引入
用法2
将所有的公用的SQL片段集中定义到一个Mapper.xml文件中,其他Mapper.xml文件如需引入mybatis-config.xml,通过命名空间.id即可。
sqlMapper.xml
在全局的mybatis-config.xml里面引入sqlMapper.xml
if
进行判断。
需求1:查询男性用户,如果输入了姓名,进行模糊查找,如果不输入就按男性用户来查询。
使用# {}的方式
1:使用 的 方 式 进 行 取 出 数 据 , 要 求 在 s q l 语 句 中 ‘ {}的方式进行取出数据,要求在sql语句中 ‘% 的方式进行取出数据,要求在sql语句中‘{}%’
2: 使用#{} 去取出数据,要求在传递参数的时候,就把% 进行拼接到参数上。%张%
choose,when,otherwise 相当于java中的 if, else if的逻辑
查询男性用户,如果输入了姓名则按照姓名模糊查找,否则如果输入了年龄则按照年龄查找。
如果其中的一个when 成立,则后的都不执行,如果所有when的都不成立,那么就执行otherwise
也就是谁在前面谁优先
作用:完成WHERE和SET关键字,并且处理SQL语句的中语法错误。
Select * from t_user where …
Update from t_user set name = …
练习:查询所有用户,如果输入了姓名按照姓名进行模糊查询,如果输入年龄,按照年龄进行查询。
Where标签 代替了 where这个关键字 , 并且会把多余的and去掉
结果:把多余的and 去掉
Set 和 where的用户类似(自已测试)
Set标签 相当于 sql中 set 关键字
需求:如果名字信息不是null,则修改名字, 如果age信息不是null,同时也修改age
会自动去掉错的 ,(逗号)
练习:按照多个id查询用户信息
Select * from t_user where id in(1,2,3)
Collection:要遍历的集合, item:接受遍历集合的值in( 1,2,3)
trim元素的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某写后缀,与之对应的属性是prefix和suffix;
可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是prefixOverrides和suffixOver
select * from user
<trim prefix="WHERE" prefixOverrides="AND |OR">
<if test="name != null and name.length()>0"> AND name=#{name}</if>
<if test="gender != null and gender.length()>0"> AND gender=#{gender}</if>
</trim>
假如说name和gender的值都不为null的话,打印的SQL为:
select * from user where name = ‘xx’ and gender = ‘xx’
where后不存在and,这是因为prefixOverrides="AND |OR"代表去掉第一个and或者是or。
update user
<trim prefix="set" suffixOverrides="," suffix=" where id = #{id} ">
<if test="name != null and name.length()>0"> name=#{name} , </if>
<if test="gender != null and gender.length()>0"> gender=#{gender} , </if>
</trim>
假如说name和gender的值都不为null的话,打印的SQL为:
update user set name=‘xx’ , gender=‘xx’ where id=‘x’
在mybatis中,一级缓存默认是开启的,并且一直无法关闭(我们没法去管理一级缓存)
1、测试代码
一级缓存满足条件:
1、同一个session中
2、相同的SQL和参数
2、日志输出:
执行update,delete,insert 语句的时候 都会清空缓存 然后刷新
mybatis 的二级缓存的作用域是一个mapper的namespace ,同一个namespace中查询sql可以从缓存中命中。
二级缓存是跨session
日志输出:
因为开启了二级缓存,第二次会去二级缓存中去命中,所有不会再发送sql语句
在全局的mybatis-config.xml 中去关闭二级缓存
查询订单,并且查询出下单人的信息
sql语句分析:
我们以定单为主,不管它有没有对应上这用户都要把订单查询出来
SELECT
*
FROM
tb_order
LEFT JOIN tb_user ON tb_order.user_id = tb_user.id
where
tb_order.order_number = “20140921001”
思考:查询订单,并且查询出下单人的信息同要把两个信息放在一块去
核心思想:扩展Order对象,来映射结果集。(把两个信息放到一块去)
第三个实体类,即用户信息也有订单信息
OrderUser.java
接口定义:
OrderMapper 返回第三个实体类
OrderMapper.xml中的定义
<!-- 查询订单,并且查询出下单人的信息 -->
<select id="queryOrderUserByNumber" resultType="OrderUser">
SELECT
*
FROM
tb_order
LEFT JOIN tb_user ON tb_order.user_id = tb_user.id
where tb_order.order_number = #{orderNumber}
</select>
核心思想:面向对象的思想,在Order对象中添加User对象。 用这个
使用resultType不能完成自动映射,所以需要手动完成结果集的映射,需要使用resultMap实现。
其中user里面的id 和order里面的id 一样。哪个id是属于谁了
测试
一对多查询:查询订单,查询出下单人信息并且查询出订单详情。
一个定单可以有多个定单详情
sql:
select *
,o.id as order_id
,u.id as user_id
,d.id as detail_id
from
tb_order as o
LEFT JOIN tb_user as u on o.user_id = u.id
LEFT JOIN tb_orderdetail as d on d.order_id = o.id
where
o.order_number = '20140921003';
定义接口
接口定义:
Order类:
提供getter,setter 方法
编写mapper
定单和商品表 是多对多的对应关系
多对多查询:查询订单,查询出下单人信息并且查询出订单详情中的商品数据。
Sql分析
SELECT
*,
o.id as order_id,
u.id as user_id,
d.id as detail_id
FROM
tb_order as o
LEFT JOIN tb_user as u ON o.user_id = u.id
left join tb_orderdetail as d on d.id = o.id
left join tb_item as i on i.id = d.item_id
where
o.order_number = #{OrderNumber};
定义接口
java对象的实现:
接口:
返回order对象,里面包含了用户,订单详情,商品
编写mapper.xml
这里面就不用给itema_id起别名,因为在订单详情表中的名字就是item_id
编写测试用例
resutlType无法帮助我们自动的去完成映射,所以只有使用resultMap手动的进行映射
type 结果集对应的数据类型 id 唯一标识,被引用的时候,进行指定
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
这个拦截器插件放在配置环境的上面
可以通过Pageinfo获取分页的信息
Pageinfo.getpages :总页数
1、创建spring的配置文件applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
配置资源文件替换器
applicationContext.xml
<!-- 使用spring自带的占位符替换功能 -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- 配置资源文件 -->
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
jdbc.properties
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/springdata
username=root
password=root
配置连接池:
applicationContext.xml
<!-- 配置连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${c3p0.driverClass}"></property>
<property name="jdbcUrl" value="${c3p0.url}"></property>
<property name="user" value="${c3p0.user}"></property>
<property name="password" value="${c3p0.password}"></property>
</bean>
<!-- 配置SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--引入mybatis的总配置 -->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
</bean>
<!-- 配置mapper -->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!-- 配置mapper接口 (这种方式太麻烦)-->
<property name="mapperInterface" value="cn.yanqi.mybatis.mapper.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<!-- 配置mybatis mapper接口扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 如果配置多个mapper的路径,可以使用,多个包使用逗号分割即可: value="cn.yanqi.mybatis.mapper,cn.yanqi.mybatis.mapper" -->
<property name="basePackage" value="cn.yanqi.mybatis.mapper" />
</bean>
全部配置
测试整合是否通过:
servcie方法:
测试: