这里采用一个案例来进行分析:(下面的案例都是采用Mapper接口编程的方式来实现)
数据库准备:在数据库中创建一个Mybatis数据库,并在中创建user(用户)、orders(订单)、orderdetail(订单详情)、items(商品)表。
各表的设计:
user表:
orders表:
orderdetail表:
items表:
表与表的关系:
user/orders表:
user——>orders:一个用户可以有多个订单;一对多
orders——>user:一个订单只能由一个用户创建;一对一
orders/orderdetail表:
orders——>orderdetail:一个订单有多个订单详情,因为一个订单可能包含多个商品;一对多
orderdetail——>orders:一个订单详情只能包含在一个订单当中;一对一
orderdetail/items表:
orderdetail——>items:一个订单明细只能对应一个商品;一对一
items——>orderdetail:一个商品可以有多个订单明细;一对多。
为每个表创建一个javaBean对象:
user表对应的User类:
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//省略getter和setter方法
}
orders表对应的Orders类:
public class Orders {
private Integer id;
private Integer user_id;
private String number;
private Date createtime;
private String note;
//省略getter和setter方法
}
orderdetail表对应的Orderdetail类:
public class Orderdetail {
private Integer id;
private Integer ordersId;
private Integer itemsId;
private Integer itemsNum;
//省略getter和setter方法
}
items表对应的Items类:
public class Items {
private Integer id;
private String name;
private float price;
private String text;
private String pic;
private Date createtime;
//省略getter和setter方法
}
需求分析:
查询订单信息,关联查询用户信息。
sql语句:
确定查询主表:订单表
确定查询从表:用户表
SELECT
orders.`id`,
orders.`user_id` userId,
orders.`number`,
user.`username`,
user.`sex`
FROM
orders,
USER
WHERE orders.`user_id` = user.`id`
方式一:采用resultType结果集映射:
由于resultType结果集映射时把查询出来的结果集中的每一列和javaBean对象中每个属性进行一一对应。然而在我们创建的javaBean中Orders表并不满足查询结果集的要求,因此这里就对Orders类进行扩展。
扩展javaBean:OrdersExt
public class OrdersExt extends Orders{
private String username;
private String sex;
//省略getter和setter方法
}
编写Mapper接口:
public interface OrdersMapper {
public List findOrdersAndUser();
}
编写OrdersMapper.xml映射文件:
把OrdersMapper.xml映射文件加载到全局配置文件中SqlMapConfig.xml文件中:
我这里使用的是加载包下的所有映射文件的方式来加载映射文件;
测试类:
public class OrdersMapperTest {
private SqlSessionFactory sqlSessionFactory ;
@Before
public void setUp() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindOerdersAndUser() {
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理类
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
List lists = ordersMapper.findOrdersAndUser();
System.out.println(lists);
sqlSession.close();
}
}
方式二:采用resultMap结果集映射
使用resultMap对结果集映射,需要先声明resultMap,因为resultMap是对查询出来的结果集中的每一列进行手动指定映射到javaBean对象中哪个属性上。
修改OrdersExt类:
public class OrdersExt extends Orders{
private User user;
//省略getter和setter方法
}
修该OrdersMapper.xml映射文件:
其中映射文件中的:
修改Mapper接口:
public interface OrdersMapper {
public List findOrdersAndUserRstMap();
}
测试类:
public class OrdersMapperTest {
private SqlSessionFactory sqlSessionFactory ;
@Before
public void setUp() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindOrdersAndUserRstMap() {
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理类
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
List lists = ordersMapper.findOrdersAndUserRstMap();
sqlSession.close();
}
}
需求分析:
查询订单信息及订单明细信息。
sql语句:
查询主表:订单表
查询从表:用户表和订单详情表
SELECT
orders.`id`,
orders.`user_id`,
orders.`number`,
user.`username`,
user.`sex`,
orderdetail.`id` detailId,
orderdetail.`items_id`,
orderdetail.`items_num`
FROM
orders,
USER,
orderdetail
WHERE orders.`user_id` = user.`id`
AND orders.`id` = orderdetail.`orders_id`
编写javaBean:
这里查询信息有订单详情和用户信息,因此,Orders类不能满足我们查询是映射的需求,因此,这里同样采用对Orders类进行扩展:
public class OrdersExt extends Orders{
private User user;
private List orderdetailLists;
//省略getter和setter方法
}
编写Mapper接口:
public interface OrdersMapper {
public List findOrdersAndOrderdetail();
}
编写OrderMapper.xml映射文件:
在映射文件中使用了
加载OrderMapper.xml映射文件:
编写测试类:
private SqlSessionFactory sqlSessionFactory ;
@Before
public void setUp() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindOrdersAndOrderdetail() {
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理类
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
List lists = ordersMapper.findOrdersAndOrderdetail();
sqlSession.close();
}
}
其实多对多就是特殊的一对一的关联关系。
需求分析:
查询用户信息及用户购买的商品信息。
sql语句:
主表:user表
从表:orders、orderdetail、items
SELECT
orders.`id`,
orders.`user_id`,
orders.`number`,
user.`username`,
user.`sex`,
orderdetail.`id` detailId,
orderdetail.`items_id`,
orderdetail.`items_num`,
items.`name`,
items.`price`
FROM
orders,
USER,
orderdetail,
items
WHERE orders.`user_id` = user.`id`
AND orders.`id` = orderdetail.`orders_id`
AND orderdetail.`items_id` = items.`id`
修改user表对应的User类:
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List ordersLists;
修改orders表对应的Orders类:
public class Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
private List orderdetailList;
修改orderdetail表对应的Orderdetail类;
public class Orderdetail {
private Integer id;
private Integer ordersId;
private Integer itemsId;
private Integer itemsNum;
private Items items;
修改items表对应的iItems类:
public class Items {
private Integer id;
private String name;
private float price;
private String text;
private String pic;
private Date createtime;
编写Mapper接口:
public interface UserMapper {
public List findUserAndItems();
}
编写UserMapper.xml映射文件:
查询用户信息及其关联的商品信息是先查用户信息及关联到的订单信息再到订单详情信息最后到商品信息的一个映射过程,所以在映射文件中要注意各层级的映射关系以及映射标签的嵌套。
加载UserMapper.xml映射文件:
编写测试类:
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory ;
@Before
public void setUp() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindOrdersAndOrderdetail() {
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List lists = userMapper.findUserAndItems();
sqlSession.close();
}
}
关联查询,也就是多表查询,查询的结果集也不是一个表所对应的JavaBean对象所能进行直接映射的。因此,我们在进行关联查询要进行合理的JavaBean对象处理和扩展,保证查询出来的结果集都有所对应的JavaBean属性和之对应。这样就能保证查询出来的结果正确无误。
在关联查询中我们常用的标签有:association和collection标签。association标签是一对一关联映射所需要的标签。collection标签是一对多所需要的标签。在Mybatis中,可以理解为多对一也是特殊的一对一(如同:多个员工对应一个部门;但是也可以理解为一个员工对应一个部门,只不过有多个员工而已;可以理解为:在一个Employee对象中有一个department属性;同时又多个Employee对象,每个对象中的department对应同一个部门);多对多是特殊的一对多。
在结果集映射中,我们用的结果集映射总共有两种,分别是:resultType和resultMap;那在关联映射的时候,我们该如何选择使用哪种结果映射方式呢?其实只需要理解两种映射的不同和映射原理。resultType映射时把查询出来的结果集和对应的JavaBean属性进行一一对应。因此,在采用resultType映射,需要映射结果集的javaBean中的所有属性都是与查询结果集进行相互对应的(属性不能进行嵌套)。而使用resultMap结果集映射,则需要先声明resultMap,后使用。先声明resultMap就是制定查询出来的结果集中的列数和javaBean对象中的哪些属性进行关联映射(属性可以嵌套)。
在mybatis中只有resultMap标签的association标签和collection标签具有延迟加载的功能。
延迟加载的意思是在进行关联查询时,利用延迟加载,先加载主信息,需要关联信息时,再去按需加载关联信息。这样就会大大的提高数据库的性能,查询单表要比查询多表速度要快的多。
设置延迟加载(打开延迟加载):
Mybatis默认是不开启延迟加载的,需要我们去配置打开延迟加载。需要在全局配置文件中SqlMapConfig.xml中设置lazyLoadingEnabled、aggressiveLazyLoading
开启延迟加载:
上面已经在全局配置文件中打开了延迟加载,下面我们就通过一个案例来测试是否已经可以使用延迟加载了:
需求分析:
查询订单信息及关联的用户信息。
编码实现:
编写Javabean:
public class OrdersExt extends Orders{
private User user;
//省略getter和setter方法
}
编写Mapper接口:
public interface OrdersMapper {
public List findOrdersAndUserLazyLoading();
}
编写OrdersMapper.xml配置文件:
为了使用懒加载,把刚才的需求分解成,首先查询订单信息,然后再按需根据id查询用户信息。
这样就需要配置两个select标签语句;在进行关联查询的时候,association标签找那个的select 是把按需查询用户信息的statement的id,column的作用就是把查询出来的orders信息集合的外键user_id作为findUserById的入参。
编写测试类:
通过断点方式来测试是否使用了懒加载。在测试语句中得到lists处打断点,通过查询lists中的元素中的user属性是否为空,当执行到获取user对象后,再看lists中元素的user属性是否为空。
public class OrdersMapperTest {
private SqlSessionFactory sqlSessionFactory ;
@Before
public void setUp() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindOrdersAndUserLazyLoading() {
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper的代理类
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
List lists = ordersMapper.findOrdersAndUserLazyLoading();
lists.get(0).getUser();
sqlSession.close();
}
}
显然通过测试:在第一次获取lists集合,集合中的元素中user属性为空,当获取user后,lists集合中元素的user属性就不为空。
什么是逆向工程:
简单解释:就是通过数据库中的表,自动生成java代码;
下载逆向工程:
逆向工程的下载地址:
https://github.com/mybatis/generator/releases/tag/mybatis-generator-1.3.2
使用逆向工程:
创建项目导入jar包:
创建generator配置文件:
该配置文件放在classpath下:
该配置文件说明看注释;该配置文件可以在下载下来的逆向工程中docs中index.html中找到;
创建生成表对应类的java类:
public class Generator {
public static void main(String[] args) throws Exception{
List warnings = new ArrayList();
boolean overwrite = true;
File configFile = new File("config/generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
}
执行该类中的方法就生成了我们所需要的类: