当使用Mybatis框架实现数据库编程时,只需要:ybatis框架解决了数据库编程相关的问题,主要是简化了数据库编程。
Mybatis框架在实现过程中,会自动生成各接口的代理对象,所以,开发人员并不需要关注接口的实现问题。
在Spring Boot项目中,当需要使用Mybatis框架实现数据库编程时,需要添加:
mybatis-spring-boot-starter
mysql-connector-java
所以,在pom.xml
中添加:
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
由于添加以上依赖后,Spring Boot在启动时就会读取连接数据库的配置信息,如果未配置,则启动报错且失败,需要在src/main/resources
下的application.properties
中添加必要的配置:
spring.datasource.url=jdbc:mysql://localhost:8888
提示:以上配置中,属性名称是固定,以上示例值是错误值,但是,启动Spring Boot只是加载以上配置,并不会执行连接,所以,配置值错误并不影响启动项目。
在Spring Boot项目中,src/main/resources
下的application.properties
是默认的配置文件,项目在启动时,Spring Boot会自动从此文件中读取相关的配置信息。
在许多配置过程中,需要在application.properties
中的配置的属性的名称是固定的!
在配置数据库的连接信息时,至少需要配置spring.datasource.url
、spring.datasource.name
、spring.datasource.password
这三个属性,分别表示连接数据库的URL、登录数据库的用户名、登录数据库的密码
spring.datasource.url=jdbc:mysql://localhost:3306/mall_pms?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
为了检验配置值是否正确,可以在src/test/java
下默认的包下创建DatebaseConnectionTests
测试类,在测试类中编写测试方法,以尝试连接数据库,即可检验:
package cn.tedu.csmall.server;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.sql.DataSource;
// 测试类必须:
// 1. 在组件扫描的包下(默认的包及其子孙包)
// 2. 只有添加了@SpringBootTest注解才会加载整个项目的运行环境(包括Spring、读取配置等)
@SpringBootTest
public class DatabaseConnectionTests {
// Spring Boot自动读取application.properties的配置,并创建了数据源对象
@Autowired
DataSource dataSource;
// 连接数据库,可用于检查application.properties中的连接参数值是否正确
@Test
public void testConnection() throws Exception {
// 获取与MySQL的连接,此方法的调用会实质的连接数据库
dataSource.getConnection();
System.out.println("当你看到这句话时,表示在application.properties中的数据库连接参数的值是正确的");
}
}
阿里巴巴的建议是:每张表都应该有id
字段,且是bigint unsigned
类型,其中,bigint
对应JAVA中的long类型,unsigned
表示无符号位,将使得此字段值不会出现负数,且取值区间是原整数的2倍…以tinyint
为例,没有添加unsigned
时,取值区间是 [-128,127],添加unsigned
以后,取值区间是 [0,255]。
当id
的类型设置为bigint
时,理论上id足够使用,即使不添加unsigned
也不会出现id不够用的情况,但仍推荐添加,其目的是为了表示语义。
应该在创建表的指定编码,创建库时可以不指定编码。
在MySQL/MariaDB强烈推荐使用utf8mb4
。
如果某个字符串类型的字段的值长度变化可能较大,应该使用varchar
类型,例如用户名,如果某个字符串类型的字段的值的长度变化不大,应该使用char
类型。
注意:某些数据可能是纯数字的,但并不具备算术运算含义,也应该使用字符串类型,不要使用使用数值类型。
在使用varchar
时,指定的长度一定是“大于必要的长度”,例如:现行的标准是“用户名最多15个字符”,则建议设计为varchar(25)
或比25
更大的值,但是,也不要过于夸张,避免影响语义。
使用Mybatis时,定义的抽象方法都必须在接口中,通常,接口会使用Mapper
作为名称的最后一个单词,例如命令为BrandMapper
等。
关于抽象方法的声明原则:
Mybatis框架只要求开发人员编写接口和抽象方法,不需要开发人员编写实现类,是因为Mybatis会通过代理模式自动生成接口的实现对象,但是,它需要明确哪些接口是需要生成代理对象的。
可以在各接口上添加@Mapper
注解,在启动项目时,Mybatis会对整个项目进行扫描,对于已经添加此注解的接口,就会生成代理对象。
也可以在配置类上添加@MapperScan
注解,用于指定各接口所在的包,则Mybatis会扫描此包及其子孙包下的所有接口,并生成这些接口的代理对象。
关于@Mapper
和@MapperScan
这2种注解,只需要选择其中1种使用即可,通常推荐@MapperScan
。
注意:使用
@MapperScan
时,一定只指向Mapper接口所在的包,并确保此包下无其它接口!
提示:Mybatis框架的
@MapperScan
,与Spring框架的@ComponentScan
没有任何关系,且不会互相影响!
以实现“插入品牌数据”为例,需要执行的SQL语句大致是:
insert into pms_brand (name, pinyin, logo, description, keywords, sort, sales, product_count, comment_count, positive_comment_count, enable, gmt_create, gmt_modified) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
先在项目的默认包下创建pojo.entity.Brand
类,类中的属性应该与数据表对应:
public class Brand implements Serializable {
private Long id;
private String name;
private String pinyin;
private String logo;
private String description;
private String keywords;
private Integer sort;
private Integer sales;
private Integer productCount;
private Integer commentCount;
private Integer positiveCommentCount;
private Integer enable;
private LocalDateTime gmtCreate;
private LocalDateTime gmtModified;
// 按照POJO规范补充后续代码
}
接下来,准备接口和抽象方法,在项目的默认包下创建mapper.BrandMapper
接口,并在接口中添加抽象方法:
package cn.tedu.csmall.server.mapper;
import cn.tedu.csmall.server.pojo.entity.Brand;
public interface BrandMapper {
/**
* 插入品牌数据
* @param brand 品牌数据
* @return 受影响的行数,成功插入数据时,将返回1
*/
int insert(Brand brand);
}
关于SQL语句,可以使用@Insert
等注解进行配置,但不推荐!
推荐使用XML文件配置SQL语句,此文件模版可以通过点击下载。
然后,在src/main/resources
下创建mapper
文件夹,将下载得到的zip文件解压,得到SomeMapper.xml
文件,将此XML文件复制到mapper
文件夹中。
先将SomeMapper.xml
重命名为BrandMapper.xml
。
关于此文件的配置:
namespace
属性,取值为对应的接口的全限定名
、
、
、
节点
等节点上,必须配置id
属性,取值为抽象方法的名称(不包含括号及参数)
等节点内部,配置SQL语句,SQL语句不需要使用分号结束例如配置为:
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.tedu.csmall.server.mapper.BrandMapper">
<insert id="insert">
insert into pms_brand (
name, pinyin, logo, description, keywords,
sort, sales, product_count, comment_count, positive_comment_count,
enable, gmt_create, gmt_modified
) values (
#{name}, #{pinyin}, #{logo}, #{description}, #{keywords},
#{sort}, #{sales}, #{productCount}, #{commentCount}, #{positiveCommentCount},
#{enable}, #{gmtCreate}, #{gmtModified}
)
insert>
mapper>
补充一个配置,用于告诉Mybatis框架这类XML文件的位置!在application.properties
中添加:
mybatis.mapper-locations=classpath:mapper/*.xml
另外,在插入数据时,还可以配置,得到自动编号的ID值,具体做法是在
节点上添加配置:
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
暂不关心此处的SQL语句
insert>
目标:根据id删除某个品牌
需要执行的SQL语句大致是:
delete from pms_brand where id=?
在BrandMapper
接口中添加抽象方法:
/**
* 根据品牌id,删除品牌数据
* @param id 期望删除的品牌数据的id
* @return 受影响的行数,当删除成功时,将返回1,如果无此id对应的数据,将返回0
*/
int deleteById(Long id);
在BrandMapper.xml
中配置SQL:
<delete id="deleteById">
delete from pms_brand where id=#{id}
delete>
在BrandMapperTests
中编写并执行测试:
@Test
public void testDeleteById() {
Long id = 1L;
int rows = mapper.deleteById(id);
System.out.println("删除完成,受影响的行数=" + rows);
}
在Mybatis中,有“动态SQL”的机制,它允许根据调用方法时传入的参数值不同,来生成不同的SQL语句。
目标:根据若干个id
一次性删除多个品牌。
需要执行的SQL语句大致是:
delete from pms_brand where id=? or id=? or id=?;
或者:
delete from pms_brand where id in (?, ?, ?);
注意:以上SQL语句中的id
的数量是不确定的。
在BrandMapper
接口中,抽象方法可以是:
int deleteByIds(Long... ids); // 注意:可变参数在处理时,本质上就是数组
或者:
int deleteByIds(Long[] ids);
或者:
int deleteByIds(List<Long> ids);
在BrandMapper.xml
中配置SQL语句:
<delete id="deleteByIds">
delete from pms_brand where id in (
<foreach collection="array" item="id" separator=",">
#{id}
foreach>
)
delete>
由于需要对参数ids
(若干个id
)进行遍历,需要使用到动态SQL中的
节点,此节点可以对数组或集合进行遍历!关于
的配置:
collection
属性:表示被遍历的参数对象,当抽象方法的参数只有1个,且没有添加@Param
注解时,当参数值的类型是数组时,此属性值为array
,当参数值的类型是List
时,此属性值为list
;否则,此属性值为@Param
注解中的参数值item
属性:表示被遍历到的元素的名称,是自定义的名称,在
内部,使用#{}
格式的占位符时,也使用此属性来表示每个元素separator
属性:表示遍历过程中各元素值之间的分隔符号最后,在BrandMapperTests
中编写并执行测试:
@Test
public void testDeleteByIds() {
int rows = mapper.deleteByIds(1L, 3L, 5L, 7L, 9L);
System.out.println("批量删除完成,受影响的行数=" + rows);
}
目标:实现根据id修改pms_brand
表中某条数据的name
字段值。
需要执行的SQL语句大致是:
update pms_brand set name=? where id=?
在BrandMapper
接口中添加抽象方法:
/**
* 根据id修改品牌的名称
* @param id 品牌id
* @param name 新的品牌名称
* @return 受影响的行数,当修改成功时,将返回1,如果无此id对应的数据,将返回0
*/
int updateNameById(@Param("id") Long id, @Param("name") String name);
在BrandMapper.xml
中配置SQL:
<update id="updateNameById">
update pms_brand set name=#{name} where id=#{id}
update>
在BrandMapperTests
中编写并执行测试:
@Test
public void testUpdateNameById() {
Long id = 3L;
String name = "王舞";
int rows = mapper.updateNameById(id, name);
System.out.println("修改品牌名称完成,受影响的行数=" + rows);
}
绑定异常,异常提示信息如下:
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): cn.tedu.csmall.server.mapper.BrandMapper.insert
出现此异常是因为找不到与抽象方法对应的SQL语句,原因可能有:
节点的namespace
属性值有误
或类似节点的id
属性值有误application.properties
中配置的mybatis.mapper-locations
属性有误pom.xml
中的依赖代码是否正确,如果无误,则删除本地仓库并重新下载