Mybatis---从入门到深化
创始人
2024-01-25 17:15:51
0

一、什么是框架?

框架即一个半成品软件。开发者从头开发一个软件需要花费大量精力,于是有一些项目组开发出半成品软件,开发者在这些软件的基础上进行开发,这样的软件就称之为框架。

如果将开发完成的软件比作是一套已经装修完毕的新房,框架就好比是一套已经修建好的毛坯房。用户直接购买毛坯房,保证建筑质量和户型合理的同时可以进行风格的自由装修。

使用框架开发的好处:

  1. 省去大量的代码编写、减少开发时间、降低开发难度。
  2. 限制程序员必须使用框架规范开发,增强代码的规范性,降低程序员之间沟通及日后维护的成本。
  3. 将程序员的注意力从技术中抽离出来,更集中在业务层面。

使用框架就好比和世界上最优秀的软件工程师共同完成一个项目,并且他们完成的还是基础、全局的工作。

二、什么是ORM框架?

ORM(Object Relationl Mapping),对象关系映射,即在数据库和对象之间作映射处理。

之前我们使用JDBC操作数据库,必须手动进行数据库和对象间的数据转换。

// 新增方法,将对象转为sql语句字段
public void AddUser(User user) throws Exception {Class.forName("com.mysql.jdbc.Driver");Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8","root", "root");String sql = "INSERT INTO user values (null,?,?,?,?)";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1,user.getName());preparedStatement.setInt(2,user.getAge());preparedStatement.setString(3,user.getAddress());preparedStatement.setString(4,user.getSex());preparedStatement.executeUpdate();// 省略资源关闭...
}// 查询方法,将数据库结果集转为对象
public List findAllUser() throws Exception {Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");PreparedStatement preparedStatement = connection.prepareStatement("select * from user");ResultSet resultSet = preparedStatement.executeQuery();//遍历查询结果集List users = new ArrayList<>();while(resultSet.next()){// 拿到每一列数据int id = resultSet.getInt("id");String name = resultSet.getString("name");int age = resultSet.getInt("age");String address = resultSet.getString("address");String sex = resultSet.getString("sex");// 将数据封装到对象中User user = new User();user.setId(id);user.setName(name);user.setAge(age);user.setAddress(address);user.setSex(sex);users.add(user);}// 省略资源关闭...return users;
}

这段代码中,数据库数据与对象数据的转换代码繁琐、无技术含量。而使用ORM框架代替JDBC后,框架可以帮助程序员自动进行转换,只要像平时一样操作对象,ORM框架就会根据映射完成对数据库的操作,极大的增强了开发效率。

三、什么是MyBatis?

MyBatis是一个半自动的ORM框架,其本质是对JDBC的封装。使用MyBatis不需要写JDBC代码,但需要程序员编写SQL语句。之前是apache的一个开源项目iBatis,2010年改名为MyBatis。

补充:

Hibernate也是一款持久层ORM框架,多年前的市场占有率很高,但近年来市场占有率越来越低。

MyBatis与Hibernate的比较:

  • MyBatis是一个半自动的ORM框架,需要手写SQL语句。
  • Hibernate是一个全自动的ORM框架,不需要手写SQL语句。
  • 使用MyBatis的开发量要大于Hibernate。

为什么Hibernate市场占有率越来越低:

  • 对于新手学习Hibernate时间成本比MyBatis大很多,MyBatis上手很快。
  • Hibernate不需要写SQL语句是因为框架来生成SQL语句。对于复杂查询,开发者很难控制生成的SQL语句,这就导致SQL调优很难进行。
  • 之前的项目功能简单,数据量小,所以使用Hibernate可以快速完成开发。而近年来项目的数据量越来越大,而互联网项目对查询速度要求也很高,这就要求我们一定要精细化的调整SQL语句。此时灵活性更强,手动编写SQL语句的MyBatis慢慢代替了Hibernate使用。
  • 在高并发、大数据、高性能、高响应的互联网项目中,MyBatis是首选的持久框架。而对于对性能要求不高的比如内部管理系统等可以使用Hibernate。

四、MyBatis入门案例

环境搭建

  1. 将SQL文件导入数据库

  2. 创建maven工程,引入依赖

    org.mybatismybatis3.5.7mysqlmysql-connector-java8.0.26junitjunit4.10log4jlog4j1.2.12
    
  3.  创建mybatis核心配置文件SqlMapConfig.xml

    
    
    
    
  4. 将log4j.properties文件放入resources中,让控制台打印SQL语句。

  5. 创建实体类

    public class User {private int id;private String username;private String sex;private String address;// 省略getter/setter/构造方法/toString方法
    }
  6. 创建持久层接口和映射文件 

 6.1 在java目录创建持久层接口

public interface UserMapper {List findAll();
}

6.2 在resource目录创建映射文件




6.3 将映射文件配置到mybatis核心配置文件中


     

映射文件注意事项:

  • 映射文件要和接口名称相同。

  • 映射文件要和接口的目录结构相同。

  • 映射文件中namespace属性要写接口的全名。

  • 映射文件中标签的id属性是接口方法的方法名。

  • 映射文件中标签的resultType属性是接口方法的返回值类型。

  • 映射文件中标签的parameterType属性是接口方法的参数类型。

  • 映射文件中resultType、parameterType属性要写全类名,如果是集合类型,则写其泛型的全类名。

  7. 测试持久层接口方法 

@Test
public void testFindAll() throws Exception {// (1)读取核心配置文件InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");// (2)创建SqlSessionFactoryBuilder对象SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();// (3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象SqlSessionFactory factory = builder.build(is);// (4)SqlSessionFactory对象获取SqlSession对象SqlSession session = factory.openSession();// (5)SqlSession对象获取代理对象UserMapper userMapper = session.getMapper(UserMapper.class);// (6)代理对象执行方法List all = userMapper.findAll();all.forEach(System.out::println);// (7)释放资源session.close();is.close();
}

 五、MyBatis核心对象及工作流程

MyBatis核心对象

  • SqlSessionFactoryBuilder

    SqlSession工厂构建者对象,使用构造者模式创建SqlSession工厂对象。

  • SqlSessionFactory

    SqlSession工厂,使用工厂模式创建SqlSession对象。

  • SqlSession

    该对象可以操作数据库,也可以使用动态代理模式创建持久层接口的代理对象操作数据库。

  • Mapper

    持久层接口的代理对象,他具体实现了持久层接口,用来操作数据库。

MyBatis工作流程

  1. 创建SqlSessionFactoryBuilder对象

  2. SqlSessionFactoryBuilder对象构建了SqlSessionFactory对象:构造者模式

  3. SqlSessionFactory对象生产了SqlSession对象:工厂模式

  4. SqlSession对象创建了持久层接口的代理对象:动态代理模式

  5. 代理对象操作数据库

六、使用SqlSession操作数据库 

除了代理对象能够操作数据库,SqlSession也能操作数据库。只是这种方式在开发中使用的较少,接下来我们使用SqlSession操作数据库:

@Test
public void testFindAll2() throws Exception {// (1)读取核心配置文件InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");// (2)创建SqlSessionFactoryBuilder对象SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();// (3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象SqlSessionFactory factory = builder.build(is);// (4)SqlSessionFactory对象获取SqlSession对象SqlSession session = factory.openSession();// (5)SqlSession直接操作数据库List users = session.selectList("com.itbaizhan.mapper.UserMapper.findAll");users.forEach(System.out::println);// (6)关闭资源session.close();is.close();
}

 七、Mapper动态代理原理

接下来我们通过源码,了解MyBatis的Mapper对象究竟是怎么生成的,他又是如何代理接口的方法。

获取代理对象

点开测试类的getMapper方法,查看该方法最终调用了什么方法。

当看到Proxy.newProxyInstance时,可以确定getMapper方法最终调用的是JDK动态代理方法,且使用MapperProxy类定义代理方式

查看代理方式

点开MapperProxy类,查看invoke方法,查看代理对象是如何工作的。

 

可以看到,MapperProxy调用了MapperMethod的execute方法定义了代理方式,且底层调用的是SqlSession的方法,根据映射文件标签不同调用不同的SqlSession方法。

结论:

  • SqlSession的getMapper方法,最终是调用的是JDK动态代理方法,生成一个代理对象,类型就是传入的接口类型。
  • MapperProxy对象通过调用MapperMethod的execute方法定义了代理方式,该方法的底层调用的是SqlSession的方法。

八、MyBatis增删改查

 8.1 新增用户

8.1.1 持久层接口添加方法

void add(User user);

8.1.2 映射文件添加标签

insert into user(username,sex,address) values(#{username},#{sex},#{address})

8.1.3 编写测试方法

@Test
public void testAdd() throws Exception {InputStream is= Resources.getResourceAsStream("SqlMapConfig.xml");SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build(is);SqlSession session = factory.openSession();UserMapper userMapper = session.getMapper(UserMapper.class);User user = new User("程序员", "男", "上海");userMapper.add(user);// 提交事务session.commit();session.close();is.close();
}

注意:

  1. 当接口方法的参数类型为POJO类型时,SQL语句中绑定参数时使用#{POJO的属性名}即可。
  2. MyBatis事务默认手动提交,所以在执行完增删改方法后,需要手动调用SqlSession对象的事务提交方法,否则数据库将不发生改变。

8.2 修改用户

优化测试类

我们发现MyBatis的测试方法在操作数据库前都需要获取代理对象,操作数据库后都需要释放资源,可以利用Junit的前置后置方法,优化测试类代码。

InputStream is = null;
SqlSession session = null;
UserMapper userMapper = null;@Before
public void before() throws IOException {// (1)读取核心配置文件is = Resources.getResourceAsStream("SqlMapConfig.xml");// (2)创建SqlSessionFactoryBuilder对象SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();// (3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象SqlSessionFactory factory = builder.build(is);// (4)SqlSessionFactory对象获取SqlSession对象session = factory.openSession();// (5)获取代理对象userMapper = session.getMapper(UserMapper.class);
}@After
public void after() throws IOException {// 释放资源session.close();is.close();
}

持久层接口添加方法

void update(User user);

 映射文件添加标签

update userset username = #{username},sex    = #{sex},address=#{address}where id = #{id}

 编写测试方法

@Test
public void testUpdate(){User user = new User(8,"程序员1","女","深圳");userMapper.update(user);session.commit();
}

 8.3 用户删除、根据Id查询

删除用户

持久层接口添加方法

void delete(int userId);

 映射文件添加标签

delete from user where id = #{id}

注:当方法的参数类型是简单数据类型时,#{}中可以写任意名称

  • 简单数据类型:基本数据类型、字符串等

编写测试方法 

@Test
public void testDelete(){userMapper.delete(8);session.commit();
}

 根据ID查询用户

持久层接口添加方法

User findById(int userId);

 映射文件添加标签

 编写测试方法

@Test
public void testFindById(){User user = userMapper.findById(1);System.out.println(user);
}

 8.4 模糊查询

8.4.1 使用#定义参数 

持久层接口添加方法

List findByNameLike(String username);

 映射文件添加标签

 编写测试方法

@Test
public void testFindByNameLike(){List users = userMapper.findByNameLike("%王%");for (User user:users){System.out.println(user);}
}

 我们看到在映射文件中,parameterType的值为string而没有写java.lang.String,这是为什么呢?

  • 参数/返回值类型为基本数据类型/包装类/String等类型时,我们可以写全类名,也可以写别名。
数据类型别名
byte_byte
long_long
short_short
int_int
int_integer
double_double
float_float
boolean_boolean
Stringstring
Bytebyte
Longlong
Shortshort
Integerint/integer
Doubledouble
Floatfloat
Booleanboolean
Datedate
BigDecimaldecimal/bigdecimal
Objectobject
Mapmap
HashMaphashmap
Listlist
ArrayListarraylist
Collectioncollection
Iteratoriterator

8.4.2 使用$定义参数 

模糊查询如果不想在调用方法时参数加%,可以使用拼接参数的方式设置Sql:

测试方法写法如下:

@Test
public void testFindByNameLike(){List users = userMapper.findByUsernameLike("程序");users.forEach(System.out::println);
}

#和$的区别:

  1. #表示sql模板的占位符,$表示将字符串拼接到sql模板中。
  2. #可以防止sql注入,一般能用#就不用$。
  3. ${}内部的参数名必须写value。

8.4.3 使用定义参数 

如果使用#还不想在调用方法的参数中添加%,可以使用允许我们在 Sql语句以外创建一个变量,并可以将其绑定到当前的Sql语句中。用法如下:

测试方法写法如下:

@Test
public void testFindByNameLike(){List users = userMapper.findByUsernameLike("程序");users.forEach(System.out::println);
}

8.5 分页查询

 

分页查询时,Sql语句使用limit关键字,需要传入开始索引和每页条数两个参数。MyBatis的多参数处理有以下方式:

顺序传参

Sql中的参数使用arg0,arg1...或param1,param2...表示参数的顺序。此方法可读性较低,在开发中不建议使用。

持久层接口方法

/*** 分页查询* @param startIndex 开始索引* @param pageSize 每页条数* @return*/
List findPage(int startIndex,int pageSize);

 映射文件

 测试类

@Test
public void testFindPage(){List users = userMapper.findPage(0,3);users.forEach(System.out::println);
}

 8.6 @Param传参

在接口方法的参数列表中通过@Param定义参数名称,在Sql语句中通过注解中所定义的参数名称指定参数位置。此方式参数比较直观的,推荐使用。

持久层接口方法

List findPage1(@Param("startIndex") int startIndex, @Param("pageSize")int pageSize);

 映射文件

 测试类

@Test
public void testFindPage1(){List users = userMapper.findPage1(3,3);users.forEach(System.out::println);
}

 8.7 POJO传参

自定义POJO类,该类的属性就是要传递的参数,在SQL语句中绑定参数时使用POJO的属性名作为参数名即可。此方式推荐使用。

自定义POJO

public class PageQuery {private int startIndex;private int pageSize;// 省略getter/setter/构造方法
}

持久层接口方法

List findPage2(PageQuery pageQuery);

 映射文件

 测试类

 

@Test
public void testFindPage2(){PageQuery pageQuery = new PageQuery(3, 3);List users = userMapper.findPage2(pageQuery);users.forEach(System.out::println);
}

8.8 Map传参

如果不想自定义POJO,可以使用Map作为传递参数的载体,在SQL语句中绑定参数时使用Map的Key作为参数名即可。此方法推荐使用。

持久层接口方法

List findPage3(Map params);

 映射文件

 测试类

@Test
public void testFindPage3(){Map params = new HashMap();params.put("startIndex",0);params.put("pageSize",4);List users = userMapper.findPage3(params);users.forEach(System.out::println);
}

 9、聚合查询、主键回填

查询用户总数

持久层接口方法

int findCount();

 映射文件

 测试类

@Test
public void testFindCount(){System.out.println(userMapper.findCount());
}

 主键回填

有时我们需要获取新插入数据的主键值。如果数据库中主键是自增的,这时我们就需要使用MyBatis的主键回填功能。

持久层接口方法

void add(User user);

映射文件

SELECT LAST_INSERT_ID();insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})

 SELECT LAST_INSERT_ID():查询刚刚插入的记录的主键值,只适用于自增主键,且必须和insert语句一起执行。

测试类 

@Test
public void testAdd(){User user = new User("尚学堂", new Date(), "男", "北京");userMapper.add(user);session.commit();System.out.println(user.getId());
}

 10、配置文件

10.1 配置文件_

MyBatis配置文件结构:

-configuration-properties(属性)-property-settings(全局配置参数)-setting-plugins(插件)-plugin-typeAliases(别名)-typeAliase-package-environments(环境)-environment-transactionManager(事务管理)-dataSource(数据源)-mappers(映射器)-mapper-package

 

 

properties

属性值定义。properties标签中可以定义属性值,也可以引入外部配置文件。无论是内部定义还是外部引入,都可以使用${name}获取值。

例如:我们可以将数据源配置写到外部的db.properties中,再使用properties标签引入外部配置文件,这样可以做到动态配置数据源。

编写db.properties 

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root

 在配置文件中引入db.properties



 当然我们也可以将数据源数据通过配置到MyBatis配置文件内,但这样做没什么意义。



 10.2 配置文件_

是配置MyBatis运行时的一些行为的,例如缓存、延迟加载、命名规则等一系列控制性参数。后期我们会使用该标签配置缓存和延迟加载等。

10.3 配置文件_

是配置MyBatis插件的。插件可以增强MyBatis功能,比如进行sql增强,打印日志,异常处理等。后期我们会使用该标签配置分页插件。

10.4 配置文件_

 

MyBatis对常用类有默认别名支持,比如java.lang.Stirng的别名为string。除此之外,我们也可以使用设置自定义别名。 

为一个类配置别名

 


此时我们即可在映射文件中使用自定义别名,如:

配置文件:

     

 映射文件

 为一个所有包下的所有类配置别名


此时该包下的所有类都有了别名,别名省略包名,和类名相同。如:

 

 

配置文件:


 映射文件:

 

相关内容

热门资讯

月薪5000社保扣多少钱 2...   现在不管是年轻人上班还是中年人都会去选择一家可以去交五险一金的公司,因为这样自己不管是贷款还是以...
第三方人力资源公司代缴社保合不...   这些年来,社保越来越受到了普通人的关注。有挺多企业特别是那种异地用工的,为了减少麻烦,为了享受以...
信用卡逾期多久算黑名单?信用卡...   信用卡在生活中使用的次数是非常多的,现在很多场所都是可以使用信用卡进行消费的,信用卡可以超额消费...
职工医疗保险个人缴费和单位缴费...   对于现今这个人人都购买保险的社会,有单位和无单位的缴纳比例都是不一样的,有单位的个人比其无单位的...
网上买二手书最全的网站 ,靠谱...   现在也比较提倡环保,有时候书这类的东西是可以循环利用的,不一定自己不用了就要扔掉或者就放在那里,...
司机乘客险是个坑,车辆座位险有...   随着国内私家车保有量的提升,拥有私家车的人越来越多,为了安全起见,不少司机都是买了不少保险,除了...
社保卡挂失后又找到了怎么办?要...     社保卡丢失了我们就应该及时的去社保局挂失,或者打电话挂失,以面出现不必要的麻烦,正式挂失手续...
杭州失业金领取条件及标准202...   如果在失业前缴纳了失业保险的话,一旦失业了,就可以领取失业保险。失业保险和个人交纳的社会保险年限...
办理社保卡需要的材料及5大步骤... 社保在我们如今的生活中起着巨大作用,办了社保卡后可以享受到许多的福利。但是很多朋友还没有办理自己的社...
新华保险交了10年退保怎么算 ...   如今,随着人民生活水平的提高和安全意识的增强,不少人都会选择给自己购置一款保险。那么如果买的保险...