接下来看一下我们的mybatis的进一步操作,熟悉一下相关配置信息,以及多参数查询,连表查询,以及分布查询的功能。
首先mybatis的中文文档就是:https://mybatis.org/mybatis-3/zh/configuration.html#environments
首先看一下三个数据库表,user,order,user_order,这是一个多对多关系。
userId对应user表的id, orderId对应order表的id
本次对于user表没有记性一对多的操作,仅查询user表信息,对于order操作进行操作一对多关系。多对多就是分别的一对多
首先看一下项目结构,lib包里面的jar包不变
utils里面的类跟之前一样,不再展示。
看一下我们的db.properties,这个就是指定我们的数据库连接信息,
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=mysql
接下来看一下我们配置信息,如果其他信息,可以到上面的文档中继续查找
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
-->
<!--定义属性以及读取属性文件-->
<properties resource="db.properties" />
<!--这个就是初始化配置-->
<settings>
<!--配置sql打印,将sql打印到控制台-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--开启驼峰命名法,默认是false,一旦开启true,那么必须符合所有的驼峰体与数据链蛇形字体的对应,否则无法进行赋值-->
<setting name="mapUnderscoreToCamelCase" value="false"/>
<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。就是我们当时的那个collection里面,如果不进行调用user,那么就不会打印信息-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
<!--指定哪个对象的方法触发一次延迟加载。-->
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
<!--定义别名-->
<typeAliases>
<!--单个别名的定义-->
<!-- <typeAlias alias="User" type="com.yang.domain.User" />-->
<!--批量定义别名,别名就是定义的类名-->
<package name="com.yang.domain" />
</typeAliases>
<!--
这个就是配置换奖,其中default可以指定选择哪个环境,这个也可以在创建连接工厂时指定
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
-->
<environments default="development">
<!--这个是开发环境配置-->
<environment id="development">
<!--使用事务-->
<transactionManager type="JDBC" />
<!--
这个指定数据库源,
pooled这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这是一种使得并发 Web 应用快速响应请求的流行处理方式。,
UNPOOLED这个数据源的实现只是每次被请求时打开和关闭连接, 不同的数据库在性能方面的表现也是不一样的,对于某些数据库来说,使用连接池并不重要,
-->
<!--从文件加载的数据库配置-->
<dataSource type="POOLED">
<!--这是 JDBC 驱动的 Java 类的完全限定名(并不是 JDBC 驱动中可能包含的数据源类-->
<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>
<!--写死的数据库配置-->
<!-- <dataSource type="UNPOOLED">-->
<!-- <!–这是 JDBC 驱动的 Java 类的完全限定名(并不是 JDBC 驱动中可能包含的数据源类–>-->
<!-- <property name="driver" value="com.mysql.jdbc.Driver" />-->
<!-- <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />-->
<!-- <property name="username" value="root" />-->
<!-- <property name="password" value="mysql" />-->
<!-- </dataSource>-->
</environment>
<!--这个是上线环境配置-->
<environment id="prod">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="!@#QAZwsx" />
</dataSource>
</environment>
</environments>
<!--加载映射文件-->
<mappers>
<!--这个是之前使用的定义单个文件映射-->
<!-- <mapper resource="com/yang/domain/Customer.xml" />-->
<!--现在使用包结构定义映射文件,需要遵从下面规范
1。名称必须要跟接口名称一致
2。必须和mapper接口在同一个目录
-->
<package name="com.yang.mapper" />
</mappers>
</configuration>
看一下javabean
// user
public class User {
private Integer id;
private String username;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
// order
public class Order {
private Integer id;
private String name;
private List<User> users = new ArrayList<>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
@Override
public String toString() {
return "Order{" +
"id=" + id +
", name='" + name + '\'' +
", users=" + users +
'}';
}
}
接下来,我们看一下mapper的编写,首先看一下基本规范
- namespace必须和Mapper接口类路径一致
- id必须和Mapper接口方法名一致
- parameterType必须和接口方法参数类型一致
- resultType必须和接口方法返回值类型一致
我们看一下UserMapper,就是一个接口类,我们使用debug看源码时,发现有使用invoke这个方法,并且jar包有cglib,这就是反射来帮助我们进行处理的,记住多参数传值必须指定param的值,否则就需要按顺序使用param1,param2.。。。
package com.yang.mapper;
import com.yang.domain.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface UserMapper {
/*
mapper接口编写规则:
1.namespace必须和Mapper接口类路径一致
2.id必须和Mapper接口方法名一致
3.parameterType必须和接口方法参数类型一致
4.resultType必须和接口方法返回值类型一致
**/
// 通过指定的ID来获取user对象
User getUserById(Integer id);
// 获取全部用户
List<User> getUsers();
// 新建用户
void insertUser(User user);
// 删除用户,可以通过指定param的value来确定xml文件中使用的参数名称
void deleteUser(@Param("id") Integer id);
// 将类按照map格式返回
Map<String, Object> getUserMapById(@Param("id") Integer id);
// 多参数传递
User getUserByArgs(@Param("id") Integer id, @Param("username") String username);
// 结果集使用resultMap
User getResultMapById(@Param("id") Integer id);
// 根据用户订单关系获取用户
User getUserThoughRelation(@Param("orderId") Integer orderId);
}
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必须与映射的类接口一致-->
<mapper namespace="com.yang.mapper.UserMapper">
<!--因为我们之前配置了别名,所以可以直接使用User来代替com.yang.domain.User-->
<select id="getUserById" resultType="User">
select * from `user` where id = #{id}
</select>
<!--获取全部用户-->
<select id="getUsers" resultType="com.yang.domain.User">
select * from `user`;
</select>
<!--
useGeneratedKeys="true" keyProperty="id" keyColumn="id" 这是另一种方法获取id值
这个跟之前写的是一样的
<selectKey keyColumn="id" keyProperty="id" resultType="Integer" order="AFTER">
select last_insert_id()
</selectKey>
-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into `user` (username, password) values (#{username}, #{password});
</insert>
<!--parameterType这个可以不用声明-->
<delete id="deleteUser">
-- delete from `user` where id=${id}
delete from `user` where id=#{id}
</delete>
<!--第一种传参方法,这个不安全,容易被sql注入-->
<!--==> Preparing: select * from `user` where id=2 -->
<select id="getUserMapById" resultType="java.util.Map">
select * from `user` where id=${id}
</select>
<!--第二种查询方法,这种可以放置sql注意,可以看出这个使用占位符插入数据-->
<!--==> Preparing: select * from `user` where id=? -->
<!-- <select id="getUserMapById" resultType="java.util.Map">-->
<!-- select * from `user` where id=#{id} -->
<!-- </select>-->
<!--这个就是传递多参数,必须使用我们当时定义的那个-->
<select id="getUserByArgs" resultType="com.yang.domain.User">
select * from `user` where id=#{id} and username=#{username}
</select>
<resultMap id="userMap" type="User">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
</resultMap>
<!--使用resultMap指定乐行,column代表数据库字段,property代表java类字段-->
<select id="getResultMapById" resultMap="userMap">
select * from `user` where id=#{id}
</select>
<!--根据用户订单关系表获取用户-->
<select id="getUserThoughRelation" resultType="com.yang.domain.User">
select * from `user` where id in(select userId from `user_order` where orderId = #{orderId})
</select>
</mapper>
接下来,我们需要看一下一对多查询,一对多查询我们可以使用一条sql语句连表查询出来,并且封装一个resultMap来进行赋值结果,或者使用分布查询,来实现,建议使用分步查询,尽量少使用连表查询
OrderMapper
package com.yang.mapper;
import com.yang.domain.Order;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface OrderMapper {
// 插入一条order
void insertOrder(Order order);
// 插入关系表
void insertRelation(@Param("userId") Integer userId, @Param("orderID") Integer orderId);
// 获取所有订单
List<Order> getAllOrder();
// 获取单个订单
Order getOrderById(Integer id);
}
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">
<!--这个namespace必须与映射的类接口一致-->
<mapper namespace="com.yang.mapper.OrderMapper">
<!--插入订单信息,并返回id-->
<insert id="insertOrder" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into `order`(name) values (#{name})
</insert>
<!--插入关系表-->
<insert id="insertRelation">
insert into `user_order`(userId, orderId) values (#{userId}, #{orderID})
</insert>
<resultMap id="ordersMap" type="Order">
<!--这个是指定字段, 对于主键可以使用id或者result,其他字段只能使用resule-->
<id column="id" property="id"/>
<result column="name" property="name"/>
<!--这个字段对应的是用户列表。因此使用collection进行赋值,指定type是list,里面的字段是User类型,然后在里面在指定赋值字段-->
<collection property="users" javaType="list" ofType="User">
<id column="userId" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
</collection>
</resultMap>
<!--通过连表查询查处所有数据-->
<select id="getAllOrder" resultMap="ordersMap">
select * from `order` as o
left join `user_order` as uo on o.id = uo.orderId
left join `user` as u on uo.userId = u.id
</select>
<resultMap id="orderMap" type="Order">
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="users" javaType="list" ofType="Order"
select="com.yang.mapper.UserMapper.getUserThoughRelation"
column="id"
/>
</resultMap>
<!--分布查询-->
<select id="getOrderById" resultMap="orderMap">
select * from `order` where id = #{id}
</select>
</mapper>
接下来看一下我们的测试
package com.yang.test;
import com.yang.domain.Order;
import com.yang.domain.User;
import com.yang.mapper.OrderMapper;
import com.yang.mapper.UserMapper;
import com.yang.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
import java.util.Map;
public class MyTest {
@Test
public void getUser() {
// 建立sql连接会话
SqlSession sqlSession = MyBatisUtils.openSession();
// 获取用户映射
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 通过定义的接口进行查询数据库
User user = userMapper.getUserById(1);
System.out.println(user); // User{id=1, username='yang', password='1234'}
List<User> users = userMapper.getUsers();
for (User user1 : users) {
System.out.println(user1);
/*
User{id=1, username='yang', password='1234'}
User{id=2, username='shi', password='5678'}
User{id=3, username='xiong', password='9012'}
*/
}
// 增加用户
User newUser = new User();
newUser.setUsername("mark");
newUser.setPassword("1111");
userMapper.insertUser(newUser);
System.out.println(newUser); // User{id=7, username='mark', password='1111'}
sqlSession.commit();
// 删除用户
userMapper.deleteUser(8);
sqlSession.commit();
// 关闭连接
sqlSession.close();
}
@Test
public void getUserMap() {
// 建立sql连接会话
SqlSession sqlSession = MyBatisUtils.openSession();
// 获取用户映射
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 通过定义的接口进行查询数据库
Map<String, Object> user = userMapper.getUserMapById(2);
System.out.println(user); // {password=5678, id=2, username=shi}
// 关闭连接
sqlSession.close();
}
@Test
public void getUseByArgs() {
// 建立sql连接会话
SqlSession sqlSession = MyBatisUtils.openSession();
// 获取用户映射
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 通过定义的接口进行查询数据库
User user = userMapper.getUserByArgs(2, "shi");
System.out.println(user); // User{id=2, username='shi', password='5678'}
// 关闭连接
sqlSession.close();
}
@Test
public void getResultMap() {
// 建立sql连接会话
SqlSession sqlSession = MyBatisUtils.openSession();
// 获取用户映射
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getResultMapById(2);
System.out.println(user); // User{id=2, username='shi', password='5678'}
sqlSession.close();
}
@Test
public void orders() {
// 建立sql连接会话
SqlSession sqlSession = MyBatisUtils.openSession();
// 获取用户映射
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 获取订单映射
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
User user = userMapper.getUserById(3);
Order order = new Order();
order.setName("订单三");
order.getUsers().add(user);
// 添加订单
orderMapper.insertOrder(order);
// 添加关系映射
orderMapper.insertRelation(user.getId(), order.getId());
// 提交事务
sqlSession.commit();
// 查询所有的订单
List<Order> allOrder = orderMapper.getAllOrder();
for (Order order1 : allOrder) {
System.out.println(order1);
/*
Order{id=3, name='订单三', users=[User{id=2, username='shi', password='5678'}, User{id=6, username='mark', password='1111'}]}
Order{id=4, name='订单三', users=[User{id=3, username='xiong', password='9012'}, User{id=2, username='shi', password='5678'}]}
Order{id=5, name='订单三', users=[User{id=3, username='xiong', password='9012'}]}
Order{id=6, name='订单三', users=[User{id=3, username='xiong', password='9012'}]}
Order{id=1, name='订单一', users=[]}
Order{id=2, name='订单二', users=[]}
*/
}
sqlSession.close();
}
@Test
public void order() {
// 建立sql连接会话
SqlSession sqlSession = MyBatisUtils.openSession();
// 获取订单映射
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
// 通过分步查询获取订单
Order order = orderMapper.getOrderById(2);
System.out.println(order); // Order{id=3, name='订单三', users=[User{id=2, username='shi', password='5678'}, User{id=6, username='mark', password='1111'}]}
System.out.println(order);
sqlSession.close();
}
}
源码地址都是前面提到的GitHub上,有兴趣的可以去看。