若有错,请指出
第二章 搭建Springboot环境,配置视图解析器jsp页面
第三章 全注解下的Spring Ioc
第四章 约定编程-Spring AOP
第五章 Spring Boot的数据库编程
文章目录
5.1 配置数据源
5.1.1 配置默认数据源
H2是内嵌式数据库,可以不使用任何配置数据库的情况下(不用写drivername,username,password等详细信息)运行项目工程
新建springboot工程,添加如下配置(添加内存数据库H2,Spring Data JPA用于配置数据源)
目录结构
控制层类
@Controller
@RequestMapping("/db")
public class DatabaseController {
@Autowired
private DataSource dataSource=null;
@RequestMapping("/info")
@ResponseBody//返回json数据到页面上
public Map<String ,Object> info(){
Map<String,Object> map=new HashMap<>();
Connection connection=null;
try {
connection=dataSource.getConnection();
//DatabaseMetaData接口可以获得有关已连接到的数据库(内存数据库H2)的元数据
DatabaseMetaData metaData=connection.getMetaData();
map.put("version",metaData.getDatabaseProductVersion());
map.put("url",metaData.getURL());
map.put("product_name",metaData.getDatabaseProductName());
map.put("user_name",metaData.getUserName());
}catch (SQLException e){
e.printStackTrace();//打印异常信息
}finally {
if(connection!=null) {
try {
connection.close();//关闭连接
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return map;
}
}
运行截图
5.1.2 配置自定义数据源
1.配置MySQL自定义数据源
这里需要删h2的依赖,添加MySQL的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>`
还需要配置数据库的相关信息才能连接到数据库,增加application.properties信息
spring.datasource.url=jdbc:mysql://localhost:3306/chapter5
spring.datasource.username=root
spring.datasource.password=123456
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#最大等待连接中的数量,设0为没有限制
spring.datasource.tomcat.max-idle=10
#最大连接活动数
spring.datasource.tomcat.max-active=50
#最大等待毫秒数,单位ms,超过时间会出错误信息
spring.datasource.tomcat.max-wait=10000
#数据库连接池初始化连接数
spring.datasource.tomcat.initial-size=5
虽然上面注释掉了驱动类的配置,但是还是可以连接数据源,因为Spring Boot会尽可能判断数据源是什么类型,然后根据默认情况匹配驱动类。如果它不能匹配,我们就可以明确的配置它。因为这里是使用Tomcat自动的数据库连接池,所以可以看到上面代码中有许多tomcat字样。
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
创建数据库chapter5
运行截图
2.配置第三方DBCP2数据源
上面绑定的是Tomcat的数据源,这里我们也可以使用第三方数据源,如DBCP2数据源。
上图中绑定的是Tomcat的数据源,若希望使用第三方数据源则要修改如下信息
引入DBCP数据源依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
修改application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/chapter5
spring.datasource.username=root
spring.datasource.password=123456
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#指定数据库连接池的类型
spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
#最大等待连接中的数量,设0为没有限制
spring.datasource.dbcp2.max-idle=10
#最大连接活动数
spring.datasource.dbcp2.max-total=50
#最大等待毫秒数,单位炜ms,超过时间会出错误信息
spring.datasource.dbcp2.max-wait-millis=10000
#数据库连接池初始化连接数
spring.datasource.dbcp2.initial-size=5
断点debug,输入http://localhost:8080/db/info,这就是第三方数据源
5.2 JDBC的缺陷
下面是使用Jdbc操作数据库的案例
目录结构
注释掉dbcp的依赖和配置,使用MySQL作为自定义数据源,不然的话注解上会出现无法自动装配。有多个类型为 ‘DataSource’ 的 bean,虽然不影响结果执行。
在chapter5数据库中创建表和插入数据
comment字段是注释作用,check是约束作用,属性名的范围在1和2之间
create table t_food(
id int(12) auto_increment,
food_name varchar(20) not null,
food_type int(3) not null
comment '1-生食,2-熟食'
check(product_type in (1,2)),
note varchar(512) null,
primary key(id)
);
//插入数据
insert into t_food values(1,'椰子鸡',2,'海南菜');
insert into t_food values(2,'宫爆鸡丁',2,'四川菜');
insert into t_food values(3,'肉夹馍',2,'陕西菜');
insert into t_food values(4,'榨菜',1,'广东菜');
insert into t_food values(5,'生鸡蛋',1,'广西菜');
创建枚举类
在这里插入代码片//枚举类
public enum FoodTypeEnum {
COOKED(2,"熟食"),RAW(1,"生食");
private int code;
private String name;
FoodTypeEnum(int code,String name){
this.code=code;
this.name=name;
}
//根据code获取食物类型
public static FoodTypeEnum getFoodTypeEnum(int code){
for(FoodTypeEnum foodTypeEnum:FoodTypeEnum.values()){
if(foodTypeEnum.getCode()==code)return foodTypeEnum;
}
return null;
}
//右键生成getter和setter方法
}
创建Food的po对象用于开发人员使用比较难理解,我们可以创建VO对象(View Object)给外部人员看
Food类
public class Food {
private Long id;
private String foodName;
private FoodTypeEnum foodTypeEnum;
private String note;
//右键生成getter和setter方法
}
FoodVo类
//View Object:视图对象给用户看,pojo对象给内部开发人员看,因为有个枚举类型的属性难理解所以用vo简化含义
public class FoodVo {
private Long id;
private String foodName;
private int foodTypeId;
private String foodTypeName;
private String note;
//右键生成getter和setter方法
}
服务类JdbcService接口和实现类
public interface JdbcService {
//根据id查找食物
public Food getFood(Long id);
}
@Service
public class JdbcServiceImpl implements JdbcService {
@Autowired//注入数据源
DataSource dataSource=null;
//获取食物
@Override
public Food getFood(Long id) {
Food food=null;
Connection connection=null;
try{
connection=dataSource.getConnection();
//定义sql语句
String sql="select id,food_name,food_type,note from t_food where id=?";
//PreparedStatement可以有效防止sql注入,所以生产环境上一定要使用PreparedStatement,而不能使用Statement
//创建一个PreparedStatement对象,用于将参数化SQL语句发送到数据库
PreparedStatement preparedStatement=connection.prepareStatement(sql);
preparedStatement.setLong(1,id);
//返回数据库查询结果存在ResultSet对象供我们使用
ResultSet resultSet=preparedStatement.executeQuery();
while (resultSet.next()){
Long fid=resultSet.getLong("id");
String foodName=resultSet.getString("food_name");
int foodType=resultSet.getInt("food_type");
String note=resultSet.getString("note");
food=new Food();
food.setId(fid);
food.setFoodName(foodName);
food.setNote(note);
food.setFoodTypeEnum(FoodTypeEnum.getFoodTypeEnum(foodType));
}
}catch (SQLException e){
e.printStackTrace();
}finally {
if (connection!=null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return food;
}
}
DatabaseController类添加如下代码
//将Food对象变为FoodVo对象
public FoodVo changeToVo(Food food){
FoodVo foodVo=new FoodVo();
foodVo.setId(food.getId());
foodVo.setFoodTypeId(food.getFoodTypeEnum().getCode());
foodVo.setFoodTypeName(food.getFoodTypeEnum().getName());
foodVo.setFoodName(food.getFoodName());
foodVo.setNote(food.getNote());
return foodVo;
}
//获取数据库的食物信息,路劲还要自己添加id参数值
@RequestMapping("/food")
@ResponseBody
public FoodVo getFood(Long id){
Food food=jdbcService.getFood(id);
return changeToVo(food);
}
运行截图
通过这个例子可以看出jdbc的操作问题
5.3 JdbcTemplate操作数据库
JdbcTemplate这种方式也不算成功,实际工作较少用到。
在JdbcServiceImpl添加如下代码,控制层调用该方法即可运行了
//之前已经引入jdbc依赖了
@Autowired
private JdbcTemplate jdbcTemplate=null;
//通过JdbcTemplate获取食物
@Override
public Food getFood2(Long id) {
String sql="select id,food_name,food_type,note from t_food where id=?";
//通过实现RowMapper接口完成JdbcTemplate(即数据库到pojo对象的映射关系)
//由于RowMapper只有一个方法也就是函数式接口,可以用lambda表达式实现接口而不用另建类,比较优雅
RowMapper<Food> rowMapper=(rs,rowNum)->{
Long fid=rs.getLong("id");
String foodName=rs.getString("food_name");
int foodType=rs.getInt("food_type");
String note=rs.getString("note");
Food food=new Food();
food.setId(fid);
food.setFoodName(foodName);
food.setNote(note);
food.setFoodTypeEnum(FoodTypeEnum.getFoodTypeEnum(foodType));
return food;
};
//只返回一条记录,一般用于聚合函数的查询
Food food=jdbcTemplate.queryForObject(sql,rowMapper,id);
return food;
}
query(String sql, RowMapper rowMapper) 可以返回一组记录
queryForObject(String sql, RowMapper rowMapper, Object… args) 只返回一条记录,因此一般用于聚合函数的查询
5.4 JPA编程
从图中可以看出JPA最顶级的接口时Repository,没有定义任何方法,而JPA接口扩展JpaRepository包含了其他子接口的方法,所以只需要定义JpaRepository就可以对数据库实行增删改查等一系列操作
5.4.1 案例
目录结构
引入jpa依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
JPA维护的核心是实体(Entity Bean),我们修改Foodr类
//声明实体
@Entity(name = "food")
//映射的数据库表名
@Table(name = "t_food")
public class Food {
//标明为主键
@Id
//表明主键策略,递增
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//food_name是数据库名,与属性名不一致需要添加@Column进行映射
@Column(name = "food_name")
private String foodName;
//由于FoodTypeEnum类型是我们自定义的在数据据库表中不存在这类型,需要转换
@Convert(converter = FoodTypeEnumConverter.class)
@Column(name = "food_type")
private FoodTypeEnum foodTypeEnum;
//与数据库中的类型和属性名一致,自动映射到数据库中
private String note;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFoodName() {
return foodName;
}
public void setFoodName(String foodName) {
this.foodName = foodName;
}
public FoodTypeEnum getFoodTypeEnum() {
return foodTypeEnum;
}
public void setFoodTypeEnum(FoodTypeEnum foodTypeEnum) {
this.foodTypeEnum = foodTypeEnum;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
FoodTypeEnum转换器
//FoodTypeEnum转换器
//将实体类的属性FoodTypeEnum类型转换为数据库的整型Integer(存的是FoodTypeEnum的code属性值)
//AttributeConverter<X, Y>,X为实体类型,Y为数据库类型
public class FoodTypeEnumConverter implements AttributeConverter<FoodTypeEnum,Integer> {
@Override
public Integer convertToDatabaseColumn(FoodTypeEnum foodTypeEnum) {
return foodTypeEnum.getCode();
}
@Override
public FoodTypeEnum convertToEntityAttribute(Integer code) {
return FoodTypeEnum.getFoodTypeEnum(code);
}
}
然后就可以定义Jpa接口扩展JpaRepository便可以获得Jpa提供的方法,默认帮我们实现方法即不用自己写实现类。
interface JpaRepository<T, ID>中,T指的是类型,id是主键的数据类型
新建与数据库数据交接的dao接口,用它继承的Jpa扩展接口JpaRepository许多封装的方法来操作数据库
//实现Jpa扩展接口
@Repository
public interface JpaFoodDao extends JpaRepository<Food, Long> {
}
创建服务JpaService接口和实现类
Spring Data JPA 中 getOne、findById、findOne 的区别
public interface JpaService {
public Food getFood(Long id);
public Food insertFood(Food food);
public void deleteFood(Long id);
public Food updateFood(Food food);
public List<Food> findFood(String foodName, FoodTypeEnum foodTypeEnum);
}
@Service
public class JpaServiceImpl implements JpaService {
@Autowired
JpaFoodDao jpaFoodDao=null;
@Override
public Food getFood(Long id) {
return jpaFoodDao.getById(id);
}
@Override
public Food insertFood(Food food) {
return jpaFoodDao.save(food);
}
//我这里只能删除本来有的菜,若给存在的id页面会报错
@Override
public void deleteFood(Long id) {
jpaFoodDao.deleteById(id);
}
@Override
public Food updateFood(Food food) {
return jpaFoodDao.save(food);
}
@Override
public List<Food> findFood(String foodName, FoodTypeEnum foodTypeEnum) {
Food food=new Food();
if(!StringUtils.isEmpty(foodName))
food.setFoodName(foodName);
if(foodTypeEnum!=null)
food.setFoodTypeEnum(foodTypeEnum);
//Example按例查询允许动态创建查询,并且不需要编写包含字段名称的查询。不需要使用特定的数据库的查询语言来编写查询语句
Example<Food> example=Example.of(food);
return jpaFoodDao.findAll(example);
}
}
创建控制层
@Controller
@RequestMapping("/jpa")
public class JpaController {
// 注入JPA接口,这里不需要使用实现类
@Autowired
private JpaService jpaService = null;
public Map<String ,Object> resultMap(Boolean success,String msg){
Map<String ,Object> map=new HashMap<>();
map.put("success:",success);
map.put("msg:",msg);
return map;
}
//将Food对象变为FoodVo对象
public FoodVo changeToVo(Food food){
FoodVo foodVo=new FoodVo();
foodVo.setId(food.getId());
foodVo.setFoodTypeId(food.getFoodTypeEnum().getCode());
foodVo.setFoodTypeName(food.getFoodTypeEnum().getName());
foodVo.setFoodName(food.getFoodName());
foodVo.setNote(food.getNote());
return foodVo;
}
//将Food对象数组变为FoodVo对象数组
public List<FoodVo> changeToVoList(List<Food> foodList){
List<FoodVo> foodVoList=new ArrayList<>();
for(int i=0;i<foodList.size();i++){
FoodVo foodVo=new FoodVo();
Food food=foodList.get(i);
foodVo.setId(food.getId());
foodVo.setFoodTypeId(food.getFoodTypeEnum().getCode());
foodVo.setFoodTypeName(food.getFoodTypeEnum().getName());
foodVo.setFoodName(food.getFoodName());
foodVo.setNote(food.getNote());
foodVoList.add(foodVo);
}
return foodVoList;
}
@RequestMapping("/food/get")
@ResponseBody
public FoodVo getFood(Long id) {
// 使用JPA接口查询对象
Food food = jpaService.getFood(id);
return changeToVo(food);
}
@RequestMapping("/food/insert")
@ResponseBody
public FoodVo insertFood(String foodName, Integer foodTypeCode,String note) {
Food food=new Food();
food.setFoodName(foodName);
food.setFoodTypeEnum(FoodTypeEnum.getFoodTypeEnum(foodTypeCode));
food.setNote(note);
jpaService.insertFood(food);
return changeToVo(food);
}
@RequestMapping("/food/delete")
@ResponseBody
public Map<String ,Object> deleteFood(Long id){
Food food=this.jpaService.getFood(id);
if(food!=null){
this.jpaService.deleteFood(id);
return this.resultMap(true,"删除成功【"+id+"】");
}
return resultMap(false,"删除失败【"+id+"】不存在");
}
@RequestMapping("/food/update")
@ResponseBody
public FoodVo updateFood(Long id,String foodName,Integer foodTypeCode,String note){
Food food=new Food();
food.setFoodName(foodName);
food.setFoodTypeEnum(FoodTypeEnum.getFoodTypeEnum(foodTypeCode));
food.setNote(note);
food.setId(id);
jpaService.updateFood(food);
return changeToVo(food);
}
@RequestMapping("/food/find")
@ResponseBody
public List<FoodVo> findFood(String foodName,Integer foodTypeCode){
FoodTypeEnum foodTypeEnum=null;
if(foodTypeCode!=null)
foodTypeEnum=FoodTypeEnum.getFoodTypeEnum(foodTypeCode);
List<Food> foodList=jpaService.findFood(foodName,foodTypeEnum);
return changeToVoList(foodList);
}
}
删除菜品
插入新菜(shuru的路径是http://localhost:8080/jpa/food/update?id=6&foodName=‘辣子鸡’&foodTypeCode=1¬e=‘四川菜’)
更新菜
查找菜(也可以foodTypeCode=2)
5.4.2 分页排序
分页排序来自扩展的PagingAndSortingRepository,两个方法参数上分别有Sort类和Pageable类
为了更好的查看数据库执行语句我们先配置JPA属性在application.properties上
#配置jpa属性
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
#打印数据库sql
spring.jpa.show-sql=true
接口Jpaservice
//排序查找
List<Food> findFoodBySort(String foodName,Integer foodTypeCode,String sortField);
//分页排序
Page<Food> findFoodByPage(String foodName,Integer foodTypeCode,String sortField,Integer page,Integer size);
JpaServiceImpl
传入的参数都进行非空判断,这样在输入url的时候只有部分参数赋值不会报错
Pageable的page是第几页,默认从0开始;size每一页的大小
@Override//根据某一列字段排序查找
public List<Food> findFoodBySort(String foodName, Integer foodTypeCode, String sortField) {
Food food=new Food();
if(!StringUtils.isEmpty(foodName))
food.setFoodName(foodName);
if(foodTypeCode!=null){
FoodTypeEnum foodTypeEnum=FoodTypeEnum.getFoodTypeEnum(foodTypeCode);
food.setFoodTypeEnum(foodTypeEnum);
}
//Example按例查询允许动态创建查询,并且不需要编写包含字段名称的查询。不需要使用特定的数据库的查询语言来编写查询语句
Example<Food> example=Example.of(food);
//默认升序
//Sort sort=Sort.by(sortField);
//降序
Sort sort=null;
if(sortField!=null)sort=Sort.by(Sort.Direction.DESC,sortField);
return jpaFoodDao.findAll(example,sort);
}
@Override//分页排序
public Page<Food> findFoodByPage(String foodName, Integer foodTypeCode, String sortField, Integer page, Integer size) {
Food food=new Food();
if(!StringUtils.isEmpty(foodName))
food.setFoodName(foodName);
if(foodTypeCode!=null){
FoodTypeEnum foodTypeEnum=FoodTypeEnum.getFoodTypeEnum(foodTypeCode);
food.setFoodTypeEnum(foodTypeEnum);
}
Example<Food> example=Example.of(food);
Sort sort=null;
if(sortField!=null)sort=Sort.by(sortField);
Pageable pageable= PageRequest.of(page,size,sort);
return jpaFoodDao.findAll(example,pageable);
}
控制层JpaController
@RequestMapping("/food/sort")
@ResponseBody//排序查找
public List<FoodVo> findFoodBySort(String foodName,Integer foodTypeCode,String sortField){
List<Food> foodList=jpaService.findFoodBySort(foodName,foodTypeCode,sortField);
return changeToVoList(foodList);
}
@RequestMapping("/food/page")
@ResponseBody//分页排序
public Page<Food> findFoodByPage(String foodName, Integer foodTypeCode, String sortField, Integer page, Integer size){
Page<Food> foodList=jpaService.findFoodByPage(foodName,foodTypeCode,sortField,page,size);
return foodList;
}
运行截图
5.4 MyBatis简介
这里主要描述settings、typeAliases、typeHandlers、plugins和mappers的应用
settings(设置):他的配置改变MyBatis的底层行为,配置映射规则如自动映射、驼峰映射
typeAliases(类型别名):应为使用类全限定名会比较长,所以MyBatis会对常用类提供默认的别名或则允许我们通过typeAliases自定义别名
typeHandlers(类型处理器):在MyBatis写入和读取数据库的过程中对于不同类型的数据(对于Java是JavaType,对于数据库是JdbcType)进行自定义转换,在大部分情况下我们不需要使用自定义的typeHandler,因为MyBatis自身定义了较多的typeHandler,会自动识别JavaType和jdbcType从而实现类型转换,一般使用在枚举类型上
案例
这里是在上章的基础上进行添加操作
目录结构
引入MyBatis依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.1</version>
</dependency>
定义类别名,这样子在mapper.xml文件需要填resultType属性时就不用写类全限定名,比较简洁
由于实体Food食物类型属性是自定义枚举类型FoodTypeEnum,所以还需要创建FoodTypeEnumTypeHandler转换器类进行转换到数据库的整型(数据库存储的是枚举类型的code属性,1代表生食2代表熟食)
//用于Java的JavaType和数据库的JdbcType进行自定义转换
//声明数据库类型
@MappedJdbcTypes(value = JdbcType.INTEGER)
//声明Java类型
@MappedTypes(value = FoodTypeEnum.class)
public class FoodTypeEnumTypeHandler extends BaseTypeHandler<FoodTypeEnum> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, FoodTypeEnum foodTypeEnum, JdbcType jdbcType) throws SQLException {
preparedStatement.setInt(i,foodTypeEnum.getCode());
}
@Override
public FoodTypeEnum getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
int code=resultSet.getInt(columnName);
return FoodTypeEnum.getFoodTypeEnum(code);
}
@Override
public FoodTypeEnum getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
int code=resultSet.getInt(columnIndex);
return FoodTypeEnum.getFoodTypeEnum(code);
}
@Override
public FoodTypeEnum getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
int code=callableStatement.getInt(columnIndex);
return FoodTypeEnum.getFoodTypeEnum(code);
}
}
创建dao层MyBatisFoodDao接口和mapper文件,注解@Mapper和@Repository两者都可以用。
@Repository需要在Spring中配置扫描地址,然后生成Dao层的Bean才能被注入到Service层中。
@Mapper不需要配置扫描地址,通过xml里面的namespace里面的接口地址,生成了Bean后注入到Service层中。
@Mapper注解:在接口类上添加了@Mapper,在编译之后会生成相应的接口实现类
@MapperScan注解:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类。如果想要每个接口都要变成实现类,那么需要在每个接口类上加上@Mapper注解,比较麻烦,解决这个问题用@MapperScan
//@Repository//也可以
//标识为MyBatis的mapper
//@Mapper
public interface MyBatisFoodDao {
public Food getFood(Long id);
public int deleteFood(Long id);
public int updateFood(Food food);
public int insertFood(Food food);
public List<Food> findFood(Food food);
}
然后编写dao层对应的mapper.xml文件
- id:对应dao层的方法名
- parpaeterType:对应dao层的参数名数据类型
- resultType:sql映射文件中定义返回值类型,在getFood和findFood方法中返回值是具体对象类型,reslutType的值本应该是全限定名类型,但是前面加了@Alians所以只需写food就可以了
- #{。。。}:里面写的是Food类的属性名,由于数据库名和Java属性名不一致,所以取数据库值赋值到Java属性值需要food_name as foodName
resultType详解
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springboot.chapter5.dao.MyBatisFoodDao">
<select id="getFood" parameterType="long" resultType="food">
select id,food_name as foodName,food_type as foodTypeEnum,note from t_food where id=#{id}
</select>
<delete id="deleteFood" parameterType="Long">
delete from t_food where id=#{id}
</delete>
<update id="updateFood" parameterType="Long">
update t_food
<set>
<if test="foodName!=null">food_name=#{foodName} ,</if>
<if test="foodTypeEnum!=null">food_type=#{foodTypeEnum,typeHandler=com.springboot.chapter5.typehandler.FoodTypeEnumTypeHandler},</if>
<if test="note!=null">note=#{note}</if>
</set>
where id=#{id}
</update>
<insert id="insertFood" parameterType="food" useGeneratedKeys="true" keyProperty="id">
insert into t_food(food_name,food_type,note) values(#{foodName},
#{foodTypeEnum,typeHandler=com.springboot.chapter5.typehandler.FoodTypeEnumTypeHandler},#{note})
</insert>
<select id="findFood" resultType="food" parameterType="food">
select id,food_name as foodName,food_type as foodTypeEnum,note from t_food
<where>
<if test="foodName!=null">food_name like concat('%',#{foodName},'%') </if>
<if test="foodTypeEnum!=null">and food_type=#{foodTypeEnum
,typeHandler=com.springboot.chapter5.typehandler.FoodTypeEnumTypeHandler} </if>
</where>
</select>
</mapper>
服务接口MyBatisService和实现类
public interface MyBatisService {
public Food getFood(Long id);
public int deleteFood(Long id);
public int updateFood(Food food);
public int insertFood(Food food);
public List<Food> findFood(Food food);
}
@Service
public class MyBatisServiceImpl implements MyBatisService {
@Autowired//启动类中就装配了
MyBatisFoodDao myBatisFoodDao=null;
@Override
public Food getFood(Long id) {
return myBatisFoodDao.getFood(id);
}
@Override
public int deleteFood(Long id) {
return myBatisFoodDao.deleteFood(id);
}
@Override
public int updateFood(Food food) {
return myBatisFoodDao.updateFood(food);
}
@Override
public int insertFood(Food food) {
return myBatisFoodDao.insertFood(food);
}
@Override
public List<Food> findFood(Food food) {
return myBatisFoodDao.findFood(food);
}
}
控制层
@RequestMapping("/mybatis")
@Controller
public class MyBatisController {
@Autowired
MyBatisService myBatisService=null;
//反馈是否执行成功的信息
public Map<String ,Object> resultMap(String msg,Boolean success){
Map<String ,Object> map=new HashMap<>();
map.put("success:",success);
map.put("msg:",msg);
return map;
}
//将Food对象变为FoodVo对象
public FoodVo changeToVo(Food food){
FoodVo foodVo=new FoodVo();
foodVo.setId(food.getId());
foodVo.setFoodTypeId(food.getFoodTypeEnum().getCode());
foodVo.setFoodTypeName(food.getFoodTypeEnum().getName());
foodVo.setFoodName(food.getFoodName());
foodVo.setNote(food.getNote());
return foodVo;
}
//将Food对象数组变为FoodVo对象数组
public List<FoodVo> changeToVoList(List<Food> foodList){
List<FoodVo> foodVoList=new ArrayList<>();
for(int i=0;i<foodList.size();i++){
FoodVo foodVo=new FoodVo();
Food food=foodList.get(i);
foodVo.setId(food.getId());
foodVo.setFoodTypeId(food.getFoodTypeEnum().getCode());
foodVo.setFoodTypeName(food.getFoodTypeEnum().getName());
foodVo.setFoodName(food.getFoodName());
foodVo.setNote(food.getNote());
foodVoList.add(foodVo);
}
return foodVoList;
}
@RequestMapping("/food/get")
@ResponseBody
public FoodVo getFood(Long id){
Food food=myBatisService.getFood(id);
System.out.println(food);
return changeToVo(food);
}
@RequestMapping("/food/delete")
@ResponseBody
public Map<String,Object> deleteFood(Long id){
int result=myBatisService.deleteFood(id);
Boolean success=result>0;
String msg=success?"删除成功":"删除失败";
return resultMap(msg,success);
}
@RequestMapping("/food/update")
@ResponseBody
public Map<String,Object> updateFood(Long id,String foodName,Integer foodTypeCode,String note){
Food food=new Food();
food.setId(id);
food.setFoodName(foodName);
food.setFoodTypeEnum(FoodTypeEnum.getFoodTypeEnum(foodTypeCode));
food.setNote(note);
int result=myBatisService.updateFood(food);
Boolean success=result>0;
String msg=success?"更新成功":"更新失败";
return resultMap(msg,success);
}
@RequestMapping("/food/insert")
@ResponseBody
public FoodVo insertFood(String foodName,Integer foodTypeCode,String note){
Food food=new Food();
food.setFoodName(foodName);
food.setFoodTypeEnum(FoodTypeEnum.getFoodTypeEnum(foodTypeCode));
food.setNote(note);
int result=myBatisService.insertFood(food);
if(result>0)return changeToVo(food);
throw new RuntimeException("插入异常");
}
@RequestMapping("/food/find")
@ResponseBody
public List<FoodVo> findFood(String foodName,Integer foodTypeCode){
Food food=new Food();
food.setFoodName(foodName);
food.setFoodTypeEnum(FoodTypeEnum.getFoodTypeEnum(foodTypeCode));
List<Food> foodList=myBatisService.findFood(food);
return changeToVoList(foodList);
}
}
配置aplication.properties,从而扫描映射文件、别名文件和typeHandler,同时为了更好的查看sql执行情况(出错时也可以查看sql语句哪里错)添加日志配置。mybatis提供了几种日志实现,这里我们选用STDOUT_LOGGING
mybatis.mapper-locations=classpath:com/springboot/chapter5/mapper/*.xml
mybatis.type-aliases-package=com.springboot.chapter5.pojo
mybatis.type-handlers-package=com.springboot.chapter5.typehandler
#控制台打印mybatis执行的sql语句
mybatis.configuration.log-impl= org.apache.ibatis.logging.stdout.StdOutImpl
使用MapperFactoryBean(针对一个接口配置)装配MyBatis接口,由于这里没写AppConfig配置类去构建IOC容器,这里把装配MyBatis的代码写在启动类中
SqlSessionTmplate是SqlSession的实现类,有了SqlSessionTemplate,我们就能用来执行Dao层的Sql语句而这个实现类中有一个关键的类就是SqlSessionFactory。
SqlSessionFactory就是生产SqlSession对象的工厂,是一个接口
SqlSession是客户端和数据库服务端之间的会话信息,里面有许多操作数据库的方法
三者详解
@Autowired//绑定单个接口
private SqlSessionTemplate sqlSessionTemplate=null;
@Bean(name="myBatisFoodDao")
public MapperFactoryBean<MyBatisFoodDao> initMyBatisFoodDao(){
MapperFactoryBean<MyBatisFoodDao> myBatisFoodDaoMapperFactoryBean=new MapperFactoryBean<>();
myBatisFoodDaoMapperFactoryBean.setSqlSessionTemplate(sqlSessionTemplate);
myBatisFoodDaoMapperFactoryBean.setMapperInterface(MyBatisFoodDao.class);
return myBatisFoodDaoMapperFactoryBean;
}
这里运行文件后会出现无法找到mapper文件,由于mapper文件是放在Java包下,需要在pom.xml的添加如下配置
target是用来存放项目构建后的文件和目录、jar包、war包、编译的class文件,如果运行后没找到mapper文件说明映射出现问题常见整合mybatis错误 Invalid bound statement (not found)
<resources>
<resource>
<directory>src/main/java</directory>
<!-- 此配置不可缺,否则mybatis的Mapper.xml将会丢失 -->
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<!--指定资源的位置-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
增删改查运行截图
5.5 装配MyBatis接口的几种方式
方法一:使用MapperFactoryBean装配
@Autowired//绑定单个接口
private SqlSessionTemplate sqlSessionTemplate=null;
@Bean(name="myBatisFoodDao")
public MapperFactoryBean<MyBatisFoodDao> initMyBatisFoodDao(){
MapperFactoryBean<MyBatisFoodDao> myBatisFoodDaoMapperFactoryBean=new MapperFactoryBean<>();
myBatisFoodDaoMapperFactoryBean.setSqlSessionTemplate(sqlSessionTemplate);
myBatisFoodDaoMapperFactoryBean.setMapperInterface(MyBatisFoodDao.class);
return myBatisFoodDaoMapperFactoryBean;
}
方法二:使用@MapperFactoryBean装配
前面使用MapperFactoryBean进行装配的,这里可以用扫描的方式整合比较简洁
现在MyBatisDao添加@Mapper注解,然后在启动类上注释掉之前的装配代码
方法三:使用@MapperScan装配
如果项目中不存在多个SqlSessionFactory(或者SqlSessionTemplat),那么完全可以不配置sqlSessionFactoryRef或者sqlSessionTemplateRef,如果存在多个则需要指定。而且sqlSessionTemplateRef的优先权gaoyusqlSessionFactoryRef
@MapperScan(basePackages = "com.springboot.chapter5.dao",annotationClass = Mapper.class,
//指定SqlSessionFactory,如果sqlSessionTemplate被指定,则作废
sqlSessionFactoryRef = "sqlSessionFactory",
//指定sqlSessionTemplate,将忽略sqlSessionFactory的配置
sqlSessionTemplateRef = "sqlSessionTemplate")
若能帮助您,不妨点个赞呗😀