1、基本概念
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的 持久层框架。
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
2、pom文件的配置
要在SpringBoot框架中使用MyBatis,首先需要在pom.xml文件中添加依赖
<!-- Mybatis启动器 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
因为涉及到数据库的连接,还需要添加mysql数据库的驱动
<!-- mysql数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
最后是数据库连接池的配置。
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
以Druid数据库连接池为例,其配置方法如下:
<!-- druid 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
<exclusions>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>jconsole</artifactId>
</exclusion>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>tools</artifactId>
</exclusion>
</exclusions>
</dependency>
3、mybatis-config.xml文件的配置
项目启动时,mybatis会默认到src/main/resources文件夹下加载该配置
MyBatis 的配置文件包含了影响 MyBatis 行为甚深的设置(settings)和属性(properties)信息。
一份配置文件的实例如下:
<?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>
<!-- 配置全局属性 -->
<settings>
<!-- 使用jdbc的getGeneratedKeys获取数据库自增主键值 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 使用列标签替换列别名 默认:true -->
<setting name="useColumnLabel" value="true" />
<!-- 开启驼峰命名转换:Table{create_time} -> Entity{createTime} -->
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
</configuration>
在本项目中,其配置内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL Map Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias type="org.fh.entity.PageData" alias="pd"/>
<typeAlias type="org.fh.entity.Page" alias="page"/>
<typeAlias type="org.fh.entity.system.User" alias="user"/>
<typeAlias type="org.fh.entity.system.Menu" alias="menu"/>
<typeAlias type="org.fh.entity.system.Role" alias="role"/>
<typeAlias type="org.fh.entity.system.Dictionaries" alias="dictionaries"/>
</typeAliases>
</configuration>
类型别名是为 Java 类型设置一个短的名字。
它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
比如:pd
可以用在任何使用 org.fh.entity.PageData
的地方
4、数据源的配置(datasource+sessionfactory)
需要深入理解Spring的IoC(控制反转)和AOP(面向切面编程)
控制反转,简单点说,就是创建对象的控制权,被反转到了Spring框架上。
通常,我们实例化一个对象时,都是使用类的构造方法来new一个对象,这个过程是由我们自己来控制的,而控制反转就把new对象的工交给了Spring容器。
要了解什么是IoC,首先需要了解Spring的常用注解
@Bean:方法级别的注解
主要用在@Configuration和@Component注解的类里
@Bean注解的方法会产生一个Bean对象,该对象由Spring管理并放到IoC容器中
引用名称是方法名,也可以用@Bean(name = "beanID")指定组件名
IoC的主要实现方式有两种:依赖查找、依赖注入。
依赖注入是一种更可取的方式。
IoC容器:具有依赖注入功能的容器,可以创建对象的容器。IoC容器负责实例化、定位、配置应用程序中的对象并建立这些对象之间的依赖。
依赖注入:由IoC容器动态地将某个对象所需要的外部资源(包括对象、资源、常量数据)注入到组件(Controller, Service等)之中。简单点说,就是IoC容器会把当前对象所需要的外部资源动态的注入给我们。
Spring依赖注入的方式主要有四个,基于注解注入方式、set注入方式、构造器注入方式、静态工厂注入方式。
推荐使用基于注解注入方式,配置较少,比较方便。
IoC容器接收很多的bean,在运行的时候,有需要用到bean的地方就进行动态注入
@Autowired默认按类型进行自动装配
//使用@value读取application.properties的内容
@Value("${datasource.no1.url}")
private String url;
@Value("${datasource.no1.username}")
private String user;
@Value("${datasource.no1.password}")
private String password;
@Value("${datasource.no1.driver-class-name}")
private String driverClass;
//在IoC容器里注册一个bean
//该bean返回的是数据库的连接池
@Bean(name = "masterDataSource")
@Primary
public DataSource masterDataSource() {
DruidDataSource dataSource = new DruidDataSource();
//以下参数在application.properties文件中定义
dataSource.setDriverClassName(driverClass);
dataSource.setUrl(url);
dataSource.setUsername(user);
dataSource.setPassword(password);
return dataSource;
}
在application.properties文件中完成配置
#数据源1
datasource.no1.driver-class-name: com.mysql.cj.jdbc.Driver
datasource.no1.url=jdbc:mysql://localhost:3306/fm?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8
datasource.no1.username=root
datasource.no1.password=19960829
Spring还需要SessionFactory去做事务的提交
//扫描的 xml 目录
static final String MAPPER_LOCATION = "classpath:mybatis/dsno1/*/*.xml";
//自定义的mybatis config 文件位置
static final String CONFIG_LOCATION = "classpath:mybatis/dsno1/mybatis-config.xml";
//扫描的 实体类 目录
static final String TYPE_ALIASES_PACKAGE = "org.fh.entity";
@Bean(name = "masterSqlSessionFactory")
@Primary
public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource)throws Exception {
//创建
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
// 设置dataSource
sessionFactory.setDataSource(masterDataSource);
// 添加mapper 扫描路径
//mapper用来实现dao流程的相关操作,将前台发送过来的对数据库的请求转化为数据库可以识别的sql语句,操作数据库并返回结果,将结果映射成程序能够识别的数据,将其赋值到相关的实体类里面
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MasterDataSourceConfig.MAPPER_LOCATION));
//设置mybatis configuration 扫描路径
sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(MasterDataSourceConfig.CONFIG_LOCATION));
// 设置typeAlias 包扫描路径
sessionFactory.setTypeAliasesPackage(MasterDataSourceConfig.TYPE_ALIASES_PACKAGE);
return sessionFactory.getObject();
}
5、dao的创建
类型:interface
定义增删改查的方法
public interface EnterpriseMapper{
/**新增
* @param pd
* @throws Exception
*/
void save(PageData pd);
/**删除
* @param pd
* @throws Exception
*/
void delete(PageData pd);
/**修改
* @param pd
* @throws Exception
*/
void edit(PageData pd);
/**列表
* @param page
* @throws Exception
*/
List<PageData> datalistPage(Page page);
/**列表(全部)
* @param pd
* @throws Exception
*/
List<PageData> listAll(PageData pd);
/**通过id获取数据
* @param pd
* @throws Exception
*/
PageData findById(PageData pd);
/**批量删除
* @param ArrayDATA_IDS
* @throws Exception
*/
void deleteAll(String[] ArrayDATA_IDS);
}
6、mapper的编写
实现namespace里面对应的方法
<mapper namespace="org.fh.mapper.dsno1.enterprise.EnterpriseMapper"></mapper>
比如
/**列表
* @param page
* @throws Exception
*/
List<PageData> datalistPage(Page page);
上述方法的具体实现代码如下:
<!-- 列表 -->
<select id="datalistPage" parameterType="page" resultType="pd">
select
<include refid="Field"></include>
from
<include refid="tableName"></include> f
where 1=1
<if test="pd.KEYWORDS != null and pd.KEYWORDS != ''"><!-- 关键词检索 -->
and
(
f.ENTERPRISENAME LIKE CONCAT(CONCAT('%', #{pd.KEYWORDS}),'%')
)
</if>
</select>
接受page类型的参数,返回一个pd类型的对象,其中的键是列名,值便是结果行中的对应值。
动态SQL用法总结如下:
- 首先定义标签,用来封装SQL语句,通过标签来调用
<!--字段-->
<sql id="Field">
f.ENTERPRISENAME,
f.SOCIALCREDITCODE,
f.ENTERPRISEADDRESS,
.....
</sql>
<!--表名 -->
<sql id="tableName">
ENTER_ENTERPRISE
</sql>
2.为什么要写where 1=1?
这个1=1常用于应用程序 根据用户选择项的不同拼凑where条件时用的。
如:web界面查询用户的信息,where默认为1=1,这样用户即使不选择任何条件,sql查询也不会出错。如果用户选择了姓名,那么where变成了where 1=1 and 姓名='用户输入的姓名',如果还选择了其他的条件,就不断在where条件后追加 and语句就行了。
也可以只指定id和resultType,使用普通的SQL语句进行查询
eg:
<select id="queryArea" resultType="com.imooc.demo.entity.Area">
SELECT area_id, area_name,
priority, create_time, last_edit_time
FROM tb_area
ORDER BY priority
DESC
</select>
3.使用#{}获取变量值(一般是写在定义好的实体类里)
<insert id="insertArea" useGeneratedKeys="true" keyProperty="areaId"
keyColumn="area_id" parameterType="com.imooc.demo.entity.Area">
INSERT INTO
tb_area(area_name,priority,
create_time,last_edit_time)
VALUES
(#{areaName},#{priority},
#{createTime},#{lastEditTime})
</insert>
7、Service层的实现
Service层主要用于复杂业务逻辑的整合
首先是对事务的配置,与数据源的配置写在一起
@Bean(name = "masterTransactionManager")
@Primary
public DataSourceTransactionManager masterTransactionManager() {
return new DataSourceTransactionManager(masterDataSource());
}
service层调用dao层的方法
在Impl文件中需要使用@Transactional
注解开启事物
eg:
@Autowired
private AreaDao areaDao;
@Override
public List<Area> getAreaList() {
// 返回所有的区域信息
return areaDao.queryArea();
}
@Override
public Area getAreaById(int areaId) {
return areaDao.queryAreaById(areaId);
}
8、业务controller层
负责处理前端发起的请求并返回相应的数据,注意URL的匹配
eg:
@Autowired
private AreaService areaService;
/**
* 获取所有的区域信息
*
* @return
*/
@RequestMapping(value = "/listarea", method = RequestMethod.GET)
private Map<String, Object> listArea() {
Map<String, Object> modelMap = new HashMap<String, Object>();
List<Area> list = new ArrayList<Area>();
// 获取区域列表
list = areaService.getAreaList();
modelMap.put("areaList", list);
return modelMap;
}
/**
* 通过区域Id获取区域信息
*
* @return
*/
@RequestMapping(value = "/getareabyid", method = RequestMethod.GET)
private Map<String, Object> getAreaById(Integer areaId) {
Map<String, Object> modelMap = new HashMap<String, Object>();
// 获取区域信息
Area area = areaService.getAreaById(areaId);
modelMap.put("area", area);
return modelMap;
}
/**
* 添加区域信息
*
* @param areaStr
* @param request
* @return
* @throws IOException
* @throws JsonMappingException
* @throws JsonParseException
*/
@RequestMapping(value = "/addarea", method = RequestMethod.POST)
private Map<String, Object> addArea(@RequestBody Area area)
throws JsonParseException, JsonMappingException, IOException {
Map<String, Object> modelMap = new HashMap<String, Object>();
// 添加区域信息
modelMap.put("success", areaService.addArea(area));
return modelMap;
}