Mybatis简介
Mybatis是一款优秀的基于ORM
的半自动轻量级持久化框架,它支持定制化SQL、存储过程以及高级映射。避免了几乎所有的JDBC代码和手动设置参数以及获取结果集,可以使用简单的XML
或注解来配置和映射原生类型、接口和Java
的POJO
(Plain Old Java Objects
,普通老式Java
对象)为数据库中的记录。
ORM(对象/关系数据库映射)
全称Object/RelationMapping
,完成面向对象的编程语言到关系数据库的映射。当ORM
框架完成映射后,程序员既可以利用面向对象程序设计语言的简单易用性,又可以利用关系数据库的技术优势。ORM
把关系数据库包装成面向对象的模型。ORM
框架是面向对象设计语言与关系数据库发展不同步时的中间解决方案。采用ORM
框架后,应用程序不再直接访问底层数据库,而是以面向对象的放松来操作持久化对象,而ORM
框架则将这些面向对象的操作转换成底层SQL
操作。ORM
框架实现的效果:把对持久化对象的保存、修改、删除 等操作,转换为对数据库的操作。
Mybatis快速入门
mybatis
开发有注解开发和xml
开发两种模式,这里先介绍xml
开发,再介绍注解开发。
XML
版
1.新建maven
项目,在pom.xml
中添加相关依赖
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
- 数据库表准备(
user
)
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`birthday` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
- 创建实体类
User.java
public class User implements {
private Integer id;
private String username;
private String password;
private String birthday;
//省略getter,setter方法
}
- 创建
mapper
接口UserMapper.java
public interface UserMapper {
List<User> findAll();
void insert(User user);
void update(User user);
void deleteById(int id);
}
- 创建
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="com.lagou.dao.UserMapper">
<select id="findAll" resultType="com.lagou.domain.User">
select * from User
</select>
</mapper>
注意:默认情况下映射文件的位置在resources目录下,并且要与接口文件的包名路径保持一致, namespace必须与当前映射文件对应mapper接口的全限定类名,sql语句的id属性必须与接口文件中的方法名一一对应。
映射文件结构介绍:
6. 在
resources
目录下创建核心文件SqlMapConfig.xml
<!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管理-->
<transactionManager type="JDBC"/>
<!-- 使用mybais提供的连接池 -->
<dataSource type="POOLED">
<!-- 数据库驱动 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!-- jdbcUrl -->
<property name="url" value="jdbc:mysql:///test"/>
<!-- 用户名 -->
<property name="username" value="root"/>
<!-- 密码 -->
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 引入配置文件 -->
<mappers>
<mapper resource="com/lagou/mapper/UserMapper.xml"/>
</mappers>
</configuration>
- 创建测试类
UserTest.java
public class UserTest {
private UserMapper userMapper;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
userMapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void test() {
List<User> all = userMapper.findAll();
for (User user : all) {
System.out.println(user);
}
}
}
- 执行结果
注解版
注解开发流程与xml
版本开发流程大致相同,只是不需要再创建UserMapper.xml
映射文件了,SQL
语句直接通过注解的方式写在接口UserMapper.java
中,主要用到@Select
,@Insert
,@Update
,@Delete
注解,与XML
映射文件里边的标签对应。
public interface UserMapper {
@Insert("insert into user (username, password, birthday) values(#{username},#{password},#{birthday})")
void insertByAnnotation(User user);
@Update("update user set username = #{username} where id = #{id}")
void updateByAnnotation(User user);
@Select("select * from user")
List<User> findAllByAnnotation();
@Delete("delete from user where id = #{id}")
void deleteByAnnotation(Integer id);
}
复杂映射查询
实际开发中,我们经常遇到关联查询的情况,主要分为一对一,一对多,多对多三种情况,下面就针对这三种情况分别介绍,还是按照XML
和注解两种方式来介绍。
数据库准备
订单表(orders
):用户与订单表是一对多的关系,一个用户可以有多个订单,但是一个订单只能属于一个用户,订单与用户为一对一关系。
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ordertime` varchar(255) DEFAULT NULL,
`total` double DEFAULT NULL,
`uid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`),
CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
角色表(sys_role
):用户与角色为多对多关系,一个用户可以有多个角色,一个角色可以属于多个用户
CREATE TABLE `sys_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`rolename` varchar(255) DEFAULT NULL,
`roleDesc` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
用户角色关联表(sys_user_role
):维护用户与角色之间的多对多关联关系
CREATE TABLE `sys_user_role` (
`userid` int(11) NOT NULL,
`roleid` int(11) NOT NULL,
PRIMARY KEY (`userid`,`roleid`),
KEY `roleid` (`roleid`),
CONSTRAINT `sys_user_role_ibfk_1` FOREIGN KEY (`userid`) REFERENCES `user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `sys_user_role_ibfk_2` FOREIGN KEY (`roleid`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
XML版
一对一查询
需求:查询订单时,连带查询出改订单所对应的的用户信息
- 创建
Order.java
实体类,类中维护一个User
变量
public class Order {
private Integer id;
private String orderTime;
private Double total;
private User user;
//省略getter,setter方法
}
- 创建
OrderMapper.java
接口
public interface OrderMapper {
List<Order> findOrderAndUser();
}
- 创建
OrderMapper.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="com.lagou.dao.OrderMapper">
<resultMap id="orderMap" type="com.lagou.pojo.Order">
<result property="id" column="id"></result>
<result property="orderTime" column="ordertime"></result>
<result property="total" column="total"></result>
<association property="user" javaType="com.lagou.pojo.User">
<result property="id" column="uid"></result>
<result property="username" column="username"></result>
</association>
</resultMap>
<select id="findOrderAndUser" resultMap="orderMap">
select * from orders o,user u where o.uid = u.id
</select>
</mapper>
主要需要注意的是我们自定义了
resultMap
标签用来封装查询的结果集,将数据库字段和实体类属性一一对应,并且对于实体类中的一对一复杂类型属性使用association
标签声明映射关系以及对应的实体类。
- 创建测试类
OrderTest.java
public class OrderTest {
private OrderMapper orderMapper;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
orderMapper = sqlSession.getMapper(OrderMapper.class);
}
@Test
public void test() {
List<Order> orders = orderMapper.findOrderAndUser();
for (Order order : orders) {
System.out.println(order);
}
}
}
- 运行结果
一对多查询
查询用户时,连带查询出该用户所有的订单信息
- 在之前步骤创建的
User.java
实体类中新增属性orders
,因为是一对多,所以用List
private List<Order> orders;
//省略getter,setter方法
- 在
UserMapper.java
接口定义新方法
List<User> findUserAndOrder();
- 在
UserMapper.xml
映射文件中新增对应的SQL
语句
<resultMap id="userMapper" type="com.lagou.pojo.User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="password" column="password"></result>
<result property="birthday" column="birthday"></result>
<collection property="orders" ofType="com.lagou.pojo.Order">
<id property="id" column="oid"></id>
<result property="orderTime" column="ordertimr"></result>
<result property="total" column="total"></result>
</collection>
</resultMap>
<select id="findUserAndOrder" resultMap="userMapper">
select u.*, o.id as oid, o.ordertime,o.total,o.uid from user u left join orders o on o.uid = u.id
</select>
一对多和一对一一样需要维护一个
resultMap
返回值类型,但需要注意的是在配置一对多映射关系的时候使用collection
标签,然后就是在SQL
关联查询的时候如果遇到不同表中相同名称的字段,需要起别名以作区分。
- 在
UserTest.java
类中新增测试方法
@Test
public void test9() {
List<User> list = userMapper.findUserAndOrder();
for (User user : list) {
System.out.println(user);
}
}
- 结果
多对多查询
多对多查询步骤和一对多查询完全一致,只是查询的sql
不一样,这里就不做赘述了。
注解版
注解版查询与
xml
查询区别就是不需要编写映射文件,而是将查询语句利用注解写在mapper
接口中,所以我们只需要在mapper
接口中做修改就可以了。
一对一查询
需求:查询订单时,连带查询出改订单所对应的的用户信息
在OrderMapper.java
接口中新增基于注解的查询方法
@Results({
@Result(property = "id", column = "id"),
@Result(property = "orderTime", column = "ordertime"),
@Result(property = "total", column = "total"),
@Result(property = "user", column = "uid",javaType = User.class, one=@One(select = "com.lagou.dao.UserMapper.findById")),
})
@Select("select * from orders")
List<Order> findOrderAndUserByAnnotation();
可以看出我们使用了@Results
,@Result
,@One
三个新注解,前边两个注解很好理解,和xml
方式里边的resultMap
标签的效果一样。这里主要介绍下边的代码,也是一对一注解查询的核心代码
@Result(property = "user", column = "uid",javaType = User.class, one=@One(select = "com.lagou.dao.UserMapper.findById"))
下边一一介绍里边的各个属性
property
:对应实体类中的属性名,必须与属性名保持一致javaType
:声明该复杂类型属性的class
one
:因为是一对一查询,所以用one
@One(select ="com.lagou.dao.UserMapper.findById")
:这句话的作用是去执行com.lagou.dao.UserMapper.findById
这个方法,根据userid
查询用户对象,然后将结果关联到user
属性中,这里很容易看出我们需要在UserMapper.java
接口中再定义一个方法findById()
,并且该查询语句需要一个参数userid
。column
:这个属性的值就是给上边的@One
查询语句准备的传入参数userid
,这里的值为uid
,意思是将数据库查询出来的uid
列名作为参数
经过上边的介绍我们知道还需要在UserMapper.java
接口中再定义一个方法findById()
@Select("select * from user where id = #{id}")
User findById(Integer id);
一对多查询
查询用户时,连带查询出该用户所有的订单信息
在UserMapper.java
接口中新增基于注解的一对多查询方法
@Select("select * from user")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "username", column = "username"),
@Result(property = "password", column = "password"),
@Result(property = "birthday", column = "birthday"),
@Result(property = "orders", column = "id", javaType = List.class, many = @Many(select = "com.lagou.dao.OrderMapper.findByUid"))
})
List<User> findUserAndOrderByAnnotation();
与一对一不用的是这里我们使用了many = @Many(select = "com.lagou.dao.OrderMapper.findByUid")
,一对多使用many
和@Many
关键字,同样需要另外指定sql
语句,以及传入参数所以我们还需要在OrderMapper.java
中定义findByUid()
方法
@Select("select * from orders where uid = #{uid}")
List<Order> findByUid(Integer uid);
多对多查询
多对多查询步骤和一对多查询完全一致,只是查询的sql
不一样,这里就不做赘述了。
注解开发常用注解回顾
@Insert
: 实现新增@Update
: 实现更新@Delete
: 实现删除@Select
: 实现查询@Result
: 实现结果集封装@Results
: 可以与@Result
一起使用,封装多个结果集@One
: 实现一对一结果集封装@Many
: 实现一对多结果集封装
动态SQL
动态SQL
主要是让我们能够灵活的拼接SQL
语句,进行一些逻辑判断,或者是复杂的操作,比如批量删除等,这里再补充下Mybatis
中的常用标签
有些标签我们已经在上边内容中介绍过了,这里我们就主要介绍动态
sql
和格式化输出里边的标签的用法。
sql
和include
标签
这两个标签一般是成对出现的,sql
用来定义一段可复用的查询代码,include
用来在需要的地方引入,下面是他们的简单用法。
<sql id="selectUser">select * from user</sql>
<select id="findAll" resultType="User">
<include refid="selectUser"></include>
</select>
if
标签和
改标签主要用来执行逻辑判断,当满足条件时才执行
<select id="findBySelective" parameterType="user" resultType="user">
select * from user where
<if test="id != null">id = #{id}</if>
<if test="username != null">and username = #{username}</if>
</select>
看到上边的语句,就会发现一个问题,当id为null而username不为null时,就会出现 where and 这样的语法错误,这种情况怎么解决呢,此时就需要用到where标签
where
标签
where
标签会知道如果它包含的标签中有返回值的话,它就插入一个where
。此外,如果标签返回的内容是以 AND
或 OR
开头的,则它会剔除掉。
<select id="findBySelective" parameterType="user" resultType="user">
select * from user
<where>
<if test="id != null">and id = #{id}</if>
<if test="username != null">and username = #{username}</if>
</where>
</select>
foreach
标签
该标签主要用于构建 in
条件,可在 sql
中对集合进行遍历,常用到批量删除、添加等处理中
<select id="findByIds" parameterType="list" resultType="user">
<include refid="selectUser"></include>
<where>
<foreach collection="array" open="id in (" close=")" item="id" separator=",">#{id}</foreach>
</where>
</select>
主要属性介绍
collection
: 该属性的值有三个分别是list
、array
、map
三种,分别对应的参数类型为:List
、数组、map
集合item
: 表示在遍历过程中每一个元素的别名index
: 表示在遍历过程中的下标,用不到可以不写open
: 拼接前缀clsoe
: 拼接后缀separator
: 每个元素之间的分隔符 在UserMapper.java
接口中这样定义,参数是一个数组
List<User> findByIds(int[] ids);
choose
标签
按顺序判断 when
中的条件出否成立,如果有一个成立,则 choose
结束。当 choose
中所有 when
的条件都不满足时,则执行 otherwise
中的 sql
。类似于 Java
的 switch
语句,choose
为 switch
,when
为 case
,otherwise
则为 default
。
<select id="findBySelective" parameterType="user" resultType="user">
select * from user
<where>
<choose>
<when test="id != null">and id = #{id}</when>
<when test="username != null">and username = #{username}</when>
<otherwise>and id = 1</otherwise>
</choose>
</where>
</select>
set
标签
和where
标签一样,set
标签也是规范格式的标签主要配合if
标签使用,当我们在 update
语句中使用 if
标签时,如果最后的 if
没有执行,则或导致逗号多余错误。此时使用 set
标签可以将动态的配置 set
关键字,和剔除追加到条件末尾的任何不相关的逗号。
<update id="update" parameterType="User">
update user
<set>
<if test="username != null">username = #{username},</if>
<if test="password != null">password = #{password}</if>
</set>
where id = #{id}
</update>
trim
标签
也是一个规范格式的标签,主要属性有四个:
prefix
: 在trim
标签内sql
语句加上前缀suffix
: 在trim
标签内sql
语句加上后缀prefixOverrides
: 指定去除多余的前缀内容,如:prefixOverrides='AND | OR'
,去除trim
标签内sql
语句多余的前缀"and"
或者"or"
suffixOverrides
:指定去除多余的前缀内容
下面写给出样例,一看就会
<update id="update" parameterType="User">
update user
<trim prefix="set" suffixOverrides=",">
<if test="username != null">username = #{username},</if>
<if test="password != null">password = #{password},</if>
</trim>
where id = #{id}
</update>
可以说它就是
set
和where
标签的结合版,可以替代他们的功能。
说明
文章内容输出来源:拉勾教育Java高薪训练营课程归纳总结