Mybatis的全局文件设置
mybatis的全局配置文件mybatis-config.xml中,有很多设置和属性.如下
- configuration(配置)
properties
(属性)settings
(设置)typeAliases
(类型别名)- typeHandlers(类型处理器)
- objectFactory(对象工厂)
plugins
(插件)environments
(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- databaseIdProvider(数据库厂商标识)
mappers
(映射器)
其中有很多设置和属性,但是我们只会涉及其中一部分
properties设置
一般会使用properties设置来加载 数据库配置文件 ,可以将数据库连接信息放在独立的配置文件中,方便后期维护
位置:在resources下
名字:任意,一般是 db.properties 或者 jdbc.properties
文件格式:properties格式,该文件格式是k=v形式
内容:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/java2212
jdbc.username=root
jdbc.password=123456
在mybatis的全局配置文件中加载该文件
<!-- 根标签 -->
<configuration>
<!-- 使用外部properties文件-->
<properties resource="db.properties" />
....
<!--
jdbc连接属性,使用properties文件中的内容,
使用${key}取出value
-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qWAXsiMM-1663853746734)(day53_mybatis.assets/image-20220921101947672.png)]
typeAliases设置
在写映射文件时,resultType或者parameterType时,如果参数是类,就需要写全限定名,路径较长
解决方案:在mybatis全局配置文件中使用typeAliases设置类型别名即可
<!--类型别名-->
<typeAliases>
<typeAlias type="com.qf.model.User" alias="User"/>
</typeAliases>
设置以后,就可以在resultType和parameterType中直接使用别名,User即可
但是随着业务的发展,项目中模块增多,实体类增多,类型别名的设置也会增多,太过于繁琐,就如以下代码
<typeAliases>
<typeAlias alias="Author" type="com.qf.model.Author"/>
<typeAlias alias="Blog" type="com.qf.model.Blog"/>
<typeAlias alias="Comment" type="com.qf.model.Comment"/>
<typeAlias alias="Post" type="com.qf.model.Post"/>
<typeAlias alias="Section" type="com.qf.model.Section"/>
<typeAlias alias="Tag" type="com.qf.model.Tag"/>
</typeAliases>
解决方法:
mybatis的类型别名给出另外一种方案,可以直接指定包,该包下面的所有类都可以直接使用类名当别名:
<!-- 类型别名 -->
<typeAliases>
<package name="com.qf.model"/>
</typeAliases>
推荐使用这种方案
mappers设置
不同的加载位置
目前mapper.xml放在resources下面,但是有一部分的公司会将映射文件和接口放在一起,类似这样:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Npre3Im-1663853746736)(day53_mybatis.assets/image-20220921104024740.png)]
当文件处于这种状态时,mappers中加载位置也要变化
原本我们直接写UserMapper.xml即可,但现在位置变了,就需要写全限定名,系统才能找到对应的xml文件
<mappers>
<!-- 加载映射文件 -->
<mapper resource="com/qf/mapper/UserMapper.xml"/>
</mappers>
如果以上操作后,发现运行失败,原因在于IDEA默认只会加载resources下面的xml文件,现在xml文件在java/com/qf/mapper包下,就无法将其编译加载至项目中
解决:只需要在pom.xml文件中添加一个build标签的代码
<build> <!--IDEA的maven项目中,默认源代码目录下(java下)的xml等资源文件并不会 在编译的时候一块打包进classes文件夹,而是直接舍弃掉。 方法1:将xml或properties等配置文件放到resource下,并修 改获取配置文件的代码,比如注册mapper.xml的位置等; 方法2:如果执意java下,那么就需要在maven中添加过滤: 将java下的xml过滤--> <resources> <resource> <directory>src/main/java</directory> <includes> <include>*.xml</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
再次运行即可
mapper的另一种加载方案
随着项目模块增多,使用mapper resource=“路径” 的方式加载会变得很繁琐,每多一个配置的xml文件就需要添加一行映射代码
mybatis提供了一种方便的加载方式,可以直接加载全部的映射文件
<mappers>
<!-- 这样就可以将com.qf.mapper下的所有映射文件全部加载 -->
<package name="com.qf.mapper"/>
</mappers>
但是这样做有前提:
1 接口和映射文件必须放在同一包下
2 接口和映射文件必须同名
否则就会绑定失败,导致无法执行SQL!
BUG
编码格式问题.
1 设置idea的编码格式为UTF-8
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dSyvGw8k-1663853746736)(D:/%E5%8D%83%E5%B3%B0%E4%B8%8A%E8%AF%BE/Every%20Day%20Stage3/day53/code/day53_mybatis.assets/image-20220921113343235.png)]
2 pom中加设置
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
再次运行
日志
mbatis框架内部使用了日志框架,默认使用的slf4j日志框架。
slf4j是日志门面,真正实现日志的是log4j框架,现在就在项目中使用log4j。
1 加入依赖
<!-- 日志依赖 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2 写配置文件
位置:resources下
名称:log4j.properties
# 日志有级别: 从小到大的级别(输出内容由详至简)
# debug < info < warn < error
log4j.rootLogger=debug, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
3 运行即可发现控制台有很多日志,可以通过调整级别控制输出的内容
# 日志有级别: 从小到大的级别(输出内容由详至简)
# debug < info < warn < error
4 按如上配置好,日志会比较多,但是一般只关注SQL的运行情况,所以我们可以配置日志只打印SQL部分
# 日志有级别: 从小到大的级别(输出内容由详至简)
# debug < info < warn < error
log4j.rootLogger=error
# 设置mapper接口类或者配置文件中mapper的路径为debug
log4j.logger.com.qf.mapper=debug,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
动态SQL
动态SQL就是动态拼接SQL语句
语法:
实现代码复用
判断是否拼接
对数据进行遍历
sql和include
实现代码复用
如果映射文件中有部分代码重复率较高,可以使用标签将重复代码写进去,然后使用进行调用即可
<!-- sql片段 -->
<sql id="user_fields">
u.id id,
u.username,
u.`password`,
u.birthday,
u.phone,
u.address
</sql>
<select id="findUserById" resultType="User" parameterType="int">
select
<!-- 引用sql片段 -->
<include refid="user_fields"/>
from user u where u.id = #{id}
</select>
if和where
判断是否拼接sql语句,一般用于模糊查询
场景:模糊查询时,当没有传入关键词时,SQL语句就不拼接模糊查询的代码,若传值了,就拼接模糊查询的代码
//接口模糊查询方法
List<User> findUserByKeyword(String keyword);
<!-- 模糊查询测试 -->
<select id="findUserByKeyword" parameterType="String" resultType="User">
select
<include refid="user_fields"/>
from
user u
<!--当下方if中判断成立,此处会自动拼接where关键词,以及if内的内容-->
<!--当下方if中判断失败,则不拼接任何东西,包括where关键词-->
<where>
<if test="keyword != null and keyword != ''">
username like concat('%',#{keyword},'%')
</if>
</where>
</select>
foreach
在同一个列参考多个参数时
比如批量删除,一下传入多个id,接口方法参数就可以是数组,让sql语句同时删除多行
这时id列同时参考多个id,就可以使用foreach遍历集合
场景: 某些时候需要根据选择的不同的id来查询数据或删除数据
--同时删除多个的sql语句
select * from user where id in (29,30,31);
delete from user where id in (29,30,31);
//接口方法的参数是集合
List<User> findUserByList(List<Integer> ids);
<select id="findUserByList" parameterType="List" resultType="User">
select * from user where id in
<!--
foreach 开始遍历
collection 要遍历的集合,此处不是接口中参数名,必须是list
item 遍历的得到到变量
open 拼接左括号(
close 拼接右括号 )
separator 拼接变量间分隔符
-->
<foreach collection="list" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
//测试同时删除多个
@Test
public void findByList() throws IOException {
// 配置文件路径
String resource = "mybatis-config.xml";
// 通过配置文件,获得输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 通过流获得SqlSession工厂
// SqlSession就是一次与SQL的交互
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession( );
// 动态代理的模式
// 可以通过接口得到对应的映射文件,从而让映射文件执行
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
ArrayList<Integer> ids = new ArrayList<Integer>( );
ids.add(29);
ids.add(30);
ids.add(31);
List<User> list = mapper.findUserByList(ids);
for (User user : list) {
System.out.println(user );
}
}
多表联查
一对一查询
需求: 订单和用户是一对一,查询订单信息以及关联的用户信息.
数据库:
create table tb_order (
id int primary key auto_increment,
detail varchar(255),
createTime datetime,
uid int
)
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`birthday` date DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
`address` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=52 DEFAULT CHARSET=utf8;
实体类
public class Order {
private int id;
private String detail;
private Date createTime;
private int uid;
// setter getter
}
// User类略
改造实体类,用于在查询出结果时封装数据
public class Order {
private int id;
private String detail;
private Date createTime;
private int uid;
// 添加User类属性,用于封装查询出的User数据
private User user;
// getter和setter方法
}
写Mapper接口以及映射文件
public interface OrderMapper {
// 查询订单
List<Order> findAllOrderWithUser();
}
<mapper namespace="com.qf.mapper.OrderMapper">
<!--
一对一的封装,直接使用resultType无法封装成功
需要使用resultMap单独对其中的user属性封装
resultMap标签中的id,当前标签的唯一标识
resultMap标签中的type,最终封装的实体类
-->
<resultMap id="orderWithUser" type="Order">
<!-- id标签封装id属性
column 结果集中的列名
property是实体类属性
-->
<id column="id" property="id"/>
<!--
result标签封装其他属性
-->
<result column="detail" property="detail"/>
<result column="createTime" property="createTime"/>
<result column="uid" property="uid"/>
<!-- 下面开始进行一对一的关联映射,使用association标签 -->
<!--
property:是最终实体类中的属性
javaType: 指定属性的数据类型
-->
<association property="user" javaType="com.qf.model.User">
<!-- column列是sql查询的列
property是关联映射的User的属性
-->
<id column="uid" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="birthday" property="birthday"/>
<result column="phone" property="phone"/>
<result column="address" property="address"/>
</association>
</resultMap>
<!--
此处使用resultMap来引用的是上面resultMap标签的id
-->
<select id="findAllOrderWithUser" resultMap="orderWithUser">
SELECT
o.id,
o.detail,
o.createTime,
u.id uid,
u.username,
u.`password`,
u.birthday,
u.phone,
u.address
FROM
tb_order o,
USER u
WHERE
o.uid = u.id
</select>
</mapper>
@Test
public void one2one() throws IOException {
// 配置文件路径
String resource = "mybatis-config.xml";
// 通过配置文件,获得输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 通过流获得SqlSession工厂
// SqlSession就是一次与SQL的交互
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession( );
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List<Order> list = mapper.findAllOrderWithUser( );
for (Order order : list) {
System.out.println(order );
}
}
总结: mybatis实现一对一就是使用
restultMap内使用association来映射完成
一对多查询
需求: 用户和订单是一对多,查询一个用户信息以及关联的所有订单信息.
SQL:
SELECT
o.id,
o.detail,
o.createTime,
u.id uid,
u.username,
u.`password`,
u.birthday,
u.phone,
u.address
FROM
USER u,
tb_order o
WHERE
u.id = o.uid
AND u.id = 29
改造实体类
public class User {
private int id;
private String username;
private String password;
private Date birthday;
private String phone;
private String address;
// 添加集合属性,用于存放查询的多个订单
private List<Order> orderList;
接口和映射文件
public interface UserMapper {
User findUserWithOrder(int id);
}
<resultMap id="userWithOrder" type="User">
<id column="uid" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="birthday" property="birthday"/>
<result column="phone" property="phone"/>
<result column="address" property="address"/>
<!-- 开始一对多的封装: User类中的Order集合 -->
<!--
一对多使用<collection>标签
property 是User类的数据
ofType 是集合中存储的数据的类型
-->
<collection property="orderList" ofType="Order">
<id column="id" property="id"/>
<result column="detail" property="detail"/>
<result column="createTime" property="createTime"/>
<result column="uid" property="uid"/>
</collection>
</resultMap>
<select id="findUserWithOrder" parameterType="int" resultMap="userWithOrder">
SELECT
o.id,
o.detail,
o.createTime,
u.id uid,
u.username,
u.`password`,
u.birthday,
u.phone,
u.address
FROM
USER u,
tb_order o
WHERE
u.id = o.uid
AND u.id = #{id}
</select>
总结: mybatis实现一对多就是使用
<select id="findUserWithOrder" parameterType="int" resultMap="userWithOrder">
SELECT
o.id,
o.detail,
o.createTime,
u.id uid,
u.username,
u.`password`,
u.birthday,
u.phone,
u.address
FROM
USER u,
tb_order o
WHERE
u.id = o.uid
AND u.id = #{id}
</select>
> 总结: mybatis实现一对多就是使用
>
> restultMap内使用collection来映射完成