目录
MyBatis不仅提供了通过配置文件方式操作数据库的方法,同时也提供了使用注解方式操作数据库的方式。相对使用配置文件整合MyBatis的方式,使用注解整合MyBatis的方式有自己的优缺点:
优点有:使用注解形式开发效率相对较高,在代码中直接判断拼接比较简单方便。
缺点有:使用注解形式代码维护麻烦,SQL和代码混合修改SQL需要重新编译打包,同时代码阅读起来相对困难。
注解形式适合简单快速的开发模式例如,使用微服务架构的项目,而大型传统架构的项目使用配置文件的模式可以将SQl和代码分开,维护方便。
接下来我们实践一下如何使用注解方式整合MyBatis。和前面的章节一样,首先添加MyBatis核心依赖
- <dependency>
- <groupId>org.mybatis.spring.bootgroupId>
- <artifactId>mybatis-spring-boot-starterartifactId>
- <version>2.2.2version>
- dependency>
由于我们使用的是MySQL数据库,添加数据库连接相关依赖
- <dependency>
- <groupId>mysqlgroupId>
- <artifactId>mysql-connector-javaartifactId>
- <scope>runtimescope>
- dependency>
如果初始化创建项目时已经添加了可以跳过这一步。
注意配置MyBatis.type-aliases-package,指明Java实体类的路径
- spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_demo?useSSL=true&useUnicode=true&characterEncoding=utf-8
- spring.datasource.username=root
- spring.datasource.password=itJMF-4RObQ2
- spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
-
- MyBatis.mapper-locations=classpath:/mapper/*.xml
- MyBatis.type-aliases-package=com.cjl.chapter8.model
在启动类中,本例是Chapter8Application类上添加MapperScan注解,告诉MyBatis mapper接口 文件扫描路径
- @SpringBootApplication
- @MapperScan("com.cjl.chapter8.mapper")
- public class Chapter8Application {
-
- public static void main(String[] args) {
- SpringApplication.run(Chapter8Application.class, args);
- }
-
- }
在com.cjl.chapter8目录(包)下创建mapper和model目录,分别用于存放mapper接口文件和Java实体类。注意这里的路径需要和前面的配置内容一致
在model目录下添加User.java,创建构造函数和基本的setter/getter方法
- public class User {
- private long id;
- private String name;
- private int age;
- private int city_id;
-
- public User() {
-
- }
-
- public User(String name, int age, int city_id) {
- this.name = name;
- this.age = age;
- this.city_id = city_id;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- public int getCityId() {
- return city_id;
- }
-
- public void setCityId(int city_id) {
- this.city_id = city_id;
- }
- }
在mapper目录下添加UserMapper接口,注意这里添加了Component注解,以便Spring boot扫描。同时使用了 Select注解,添加了一条select * from user的SQL语句
- @Component
- public interface UserMapper {
- @Select("select * from user")
- List
selectAllUser(); - }
完成以上操作之后,我们就完成了一条最基本的select语句。接下来我们测试一下,看注解形式是否生效。
在测试类中添加测试代码,注意这里给userMapper添加了Autowired注解,使得Spring boot扫描到
- @Autowired
- UserMapper userMapper;
-
- @Test
- void contextLoads() {
- }
-
- @Test
- public void testSelectAllUser() {
- List
users = userMapper.selectAllUser(); - for(User user : users) {
- System.out.println(" user name: " + user.getName()
- + " age: " + user.getAge());
- }
- }
执行测试程序
成功输出查询结果,说明使用注解形式整合的MyBatis已经生效。
前面的例子SQL语句是固定的,没有参数。现在我们尝试传递一些参数试试,我们写一个按id查询User
@Select("select * from user where id=#{id}") User selectUserById(Long id);
可以看到这里参数的语法和XML配置文件是一致的。
添加测试用例
- @Test
- public void testSelectUserById() {
- User user = userMapper.selectUserById(1L);
- System.out.println(" user name: " + user.getName()
- + " age: " + user.getAge());
-
- }
运行输出
这里要注意使用#{xx}语法传入变量参数,定义的参数名必须和方法中的参数名保持一致,另外#{xx}语法支持传入多个参数。
但是有的时候参数名和数据库字段名并不一致,这个时候怎么办呢?
使用Param注解,Param注解可以为参数取个别名,先上例子
- @Select("select * from user where name=#{name} and age=#{age}")
- User selectUserByParam(@Param("name") String realName, @Param ("age") Integer realAge);
解释一下 @Param(“name”)相当于在给了参数realname一个别名,并且在传递给SQL时会转换为#{name}语法,这样就完成了参数名和字段的映射。
编写测试程序并且运行
- @Test
- public void testSelectUserByParam() {
- User user = userMapper.selectUserByParam("张华",28);
- System.out.println(" user name: " + user.getName()
- + " age: " + user.getAge());
- }
有时候传递会觉得传递多个参数较为繁琐,而且有时多个传递的参数是一个相对紧密的集合,那可以考虑使用哈希Map传递参数
- @Select("select * from user where name=#{name} and age = #{age}")
- User selectUserByMap(Map
map) ;
将SQL语句需要的参数通过map类型传入,key为参数名,value为参数值。MyBatis会自动匹配对应的映射中的参数值。注意这里Map的Value对应类型是Object,因为Value值的类型可能有多种多样。
- @Test
- public void testSelectUserByMap() {
- Map
param = new HashMap<>(); - param.put("name", "陈雨");
- param.put("age", 22);
- User user = userMapper.selectUserByMap(param);
- System.out.println(" user name: " + user.getName()
- + " age: " + user.getAge());
- }
执行结果
对于insert、update等参数较多的方法,可以使用pojo对象传参。需要注意的是,参数的名字和类型必须和pojo对象的属性保持一致
- @Insert({
- "insert into user (name, age, city_id) values ( " +
- "#{name, jdbcType=VARCHAR}, " +
- "#{age, jdbcType=INTEGER}, " +
- "#{city_id, jdbcType=INTEGER} )"
- })
- void inset(User user);
编辑测试用例
- @Test
- public void testInsertUser() {
- User usr = new User("测试", 20, 1);
- userMapper.inset(usr);
- testSelectAllUser();
- }
执行输出如下
首先我们说一下为什么要有分页查询,假如有一个数据库存储了很多数据例如上亿条,那如果没有使用分页查询的话,会存在两个问题:
查询的客户端例如小程序、APP或者浏览器的单次加载数据过多,容易导致应用崩溃
查询数据库的数据量过大,数据库传输大量的数据,导致查询的很长
对于这两个问题一般是通过分页解决,对于分页来说主要有两类逻辑分页和物联分页。
逻辑分页:本身后台还是一次性将所有数据查询出来,保存到集合中如List,后续如果有分页请求,再对List集合进行拆分,这种做法优点是减少了操作数据库的次数,但它并没有减少单次查询数据库的数据总量,单次查询数据库的时间还是会很长
物理分页:不是一次性将所有数据全部查询出来而是按当前的需要按页进行查询,例如每页需要展示.查询第一页:发送一条查询20条的SQL语句.查询下 一页数据:又发送一条查询后20条的SQL语句.有点单词查询数据库的时间非常短,但操作数据库的次数增加。
由于在一般的数据查询应用场景,用户都没有必要一次性查看所有数据,因此经常采用物理分页的方法
一般实现物理分页是采用select 命令的limit关键字实现,limit关键字有两个参数,第一个参数指定要返回的第一行的偏移量,第二个参数指定要返回的最大行数,注意初始行的偏移量是0,不是1。
SELECT * FROM tbl LIMIT 5,10; # 假设有16行则剩余6-15行
在数据展示的一侧只要知道每页展示多少,当前需要展示第几页,然后发送对应的参数到数据库进行查询即可。在实际应用中我们通常是通过插件实现自动分页的,例如MyBatis框架下也有很多插件可以实现分页功能,其中使用比较多的就是pageHelper。pageHelper提供了针对Spring Boot框架的依赖库pagehelper-spring-boot-starter,对于Spring Boot项目可以使用pagehelper-spring-boot-starter组件。
首先添加pom文件依赖
- <dependency>
- <groupId>com.github.pagehelpergroupId>
- <artifactId>pagehelper-spring-boot-starterartifactId>
- <version>1.4.3version>
- dependency>
然后 在application.properties文件中对插件进行配置,当然也支持用注解的形式进行配置
- pagehelper.helper-dialect=mysql
- pagehelper.reasonable=true
- pagehelper.support-methods-arguments=true
- pagehelper.params=count=countSql
helper-dialect:指定要处理的数据库类型,目前高版本的pageHelper也可以不配置,pageHelper插件会自动检测数据库的类型。
reasonable:显示优化参数,默认为false,设置为true时,当pageNum≤0时,默认显示第一页,当pageNum超过pageSize时,显示最后一页。
support-methods-arguments:分页插件会根据查询方法的参数,自动在params配置的字段中取值,找到合适的值会自动分页。
params:用于从对象中根据属性名取值,可以配置pageNum、pageSize,count不用配置映射的默认值。
配置完毕,我们来编写测试函数
- @Test
- public void testSelectListPaged() {
- PageHelper.startPage(1, 2);
- List
users = userMapper.selectAllUser(); - PageInfo
pageInfo = new PageInfo(users); - System.out.println("总页数:"+pageInfo.getPages()+", 总条数:" +pageInfo.getTotal()+",当前页号:"+pageInfo.getPageNum() );
- for (User user : users) {
- System.out.println("name:" + user.getName()+", age: " + user.getAge());
- }
- }
- }
执行测试程序
说明插件生效了
项目源码