若有错,请指出
第二章 搭建Springboot环境,配置视图解析器jsp页面
第三章 全注解下的Spring Ioc
第四章 约定编程-Spring AOP
第五章 Spring Boot的数据库编程

文章目录

  • 5.1 配置数据源
    • 5.1.1 配置默认数据源
    • 5.1.2 配置自定义数据源
  • 5.2 JDBC的缺陷
  • 5.3 JdbcTemplate操作数据库
    • 5.4 JPA编程
    • 5.4.1 案例
    • 5.4.2 分页排序
  • 5.4 MyBatis简介
    • 案例
  • 5.5 装配MyBatis接口的几种方式

5.1 配置数据源

5.1.1 配置默认数据源

H2是内嵌式数据库,可以不使用任何配置数据库的情况下(不用写drivername,username,password等详细信息)运行项目工程
新建springboot工程,添加如下配置(添加内存数据库H2,Spring Data JPA用于配置数据源)

目录结构

控制层类

@Controller
@RequestMapping("/db")
public class DatabaseController {@Autowiredprivate 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 nullcomment '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;//获取食物@Overridepublic 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")@ResponseBodypublic FoodVo getFood(Long id){Food food=jdbcService.getFood(id);return changeToVo(food);}

运行截图

通过这个例子可以看出jdbc的操作问题

5.3 JdbcTemplate操作数据库

JdbcTemplate这种方式也不算成功,实际工作较少用到。
在JdbcServiceImpl添加如下代码,控制层调用该方法即可运行了

    //之前已经引入jdbc依赖了@Autowiredprivate JdbcTemplate jdbcTemplate=null;//通过JdbcTemplate获取食物@Overridepublic 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> {@Overridepublic Integer convertToDatabaseColumn(FoodTypeEnum foodTypeEnum) {return foodTypeEnum.getCode();}@Overridepublic 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 {@AutowiredJpaFoodDao jpaFoodDao=null;@Overridepublic Food getFood(Long id) {return jpaFoodDao.getById(id);}@Overridepublic Food insertFood(Food food) {return jpaFoodDao.save(food);}//我这里只能删除本来有的菜,若给存在的id页面会报错@Overridepublic void deleteFood(Long id) {jpaFoodDao.deleteById(id);}@Overridepublic Food updateFood(Food food) {return jpaFoodDao.save(food);}@Overridepublic 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接口,这里不需要使用实现类@Autowiredprivate 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")@ResponseBodypublic FoodVo getFood(Long id) {// 使用JPA接口查询对象Food food = jpaService.getFood(id);return changeToVo(food);}@RequestMapping("/food/insert")@ResponseBodypublic 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")@ResponseBodypublic 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")@ResponseBodypublic 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")@ResponseBodypublic 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&note=‘四川菜’)

更新菜

查找菜(也可以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> {@Overridepublic void setNonNullParameter(PreparedStatement preparedStatement, int i, FoodTypeEnum foodTypeEnum, JdbcType jdbcType) throws SQLException {preparedStatement.setInt(i,foodTypeEnum.getCode());}@Overridepublic FoodTypeEnum getNullableResult(ResultSet resultSet, String columnName) throws SQLException {int code=resultSet.getInt(columnName);return FoodTypeEnum.getFoodTypeEnum(code);}@Overridepublic FoodTypeEnum getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {int code=resultSet.getInt(columnIndex);return FoodTypeEnum.getFoodTypeEnum(code);}@Overridepublic 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文件

  1. id:对应dao层的方法名
  2. parpaeterType:对应dao层的参数名数据类型
  3. resultType:sql映射文件中定义返回值类型,在getFood和findFood方法中返回值是具体对象类型,reslutType的值本应该是全限定名类型,但是前面加了@Alians所以只需写food就可以了
  4. #{。。。}:里面写的是Food类的属性名,由于数据库名和Java属性名不一致,所以取数据库值赋值到Java属性值需要food_name as foodName
    resultType详解
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//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;@Overridepublic Food getFood(Long id) {return myBatisFoodDao.getFood(id);}@Overridepublic int deleteFood(Long id) {return myBatisFoodDao.deleteFood(id);}@Overridepublic int updateFood(Food food) {return myBatisFoodDao.updateFood(food);}@Overridepublic int insertFood(Food food) {return myBatisFoodDao.insertFood(food);}@Overridepublic List<Food> findFood(Food food) {return myBatisFoodDao.findFood(food);}
}

控制层

@RequestMapping("/mybatis")
@Controller
public class MyBatisController {@AutowiredMyBatisService 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")@ResponseBodypublic FoodVo getFood(Long id){Food food=myBatisService.getFood(id);System.out.println(food);return changeToVo(food);}@RequestMapping("/food/delete")@ResponseBodypublic 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")@ResponseBodypublic 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")@ResponseBodypublic 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")@ResponseBodypublic 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")

若能帮助您,不妨点个赞呗

第五章 Spring Boot的数据库编程相关推荐

  1. 19年8月 字母哥 第一章 spring boot 2.x基础及概念入门 这里全部看完了 热部署没出来 第二章在前面2页 用热点公司网不行

    http://springboot.zimug.com/1233100   文档 http://www.zimug.com/page/5     字母哥个人博客 11111 第一章 spring bo ...

  2. 《Spring Boot极简教程》第8章 Spring Boot集成Groovy,Grails开发

    第8章 Spring Boot集成Groovy,Grails开发 本章介绍Spring Boot集成Groovy,Grails开发.我们将开发一个极简版的pms(项目管理系统). Groovy和Gra ...

  3. 第五章 Spring进阶-注解方式实现AOP(1)

    <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 徒弟:师傅,我 ...

  4. 《深入理解 Spring Cloud 与微服务构建》第十六章 Spring Boot Security 详解

    <深入理解 Spring Cloud 与微服务构建>第十六章 Spring Boot Security 详解 文章目录 <深入理解 Spring Cloud 与微服务构建>第十 ...

  5. Spring Boot 2.0 配置图文教程第 2 章 Spring Boot 配置## 书信息 demo.book.name=[Spring Boot 2.x Core Action] demo.b

    本章内容 1.自定义属性快速入门 2.外化配置 3.自动配置 4.自定义创建 Starter 组件 摘录:读书是读完这些文字还要好好用心去想想,写书也一样,做任何事也一样 第 2 章 Spring B ...

  6. Spring Boot (十五): Spring Boot + Jpa + Thymeleaf 增删改查示例

    <p>这篇文章介绍如何使用 Jpa 和 Thymeleaf 做一个增删改查的示例.</p> 先和大家聊聊我为什么喜欢写这种脚手架的项目,在我学习一门新技术的时候,总是想快速的搭 ...

  7. SpringBoot 系列教程(八十五):Spring Boot使用MD5加盐验签Api接口之前后端分离架构设计

    加密算法参考: 浅谈常见的七种加密算法及实现 加密算法参考: 加密算法(DES,AES,RSA,MD5,SHA1,Base64)比较和项目应用 目的: 通过对API接口请求报文签名,后端进行验签处理, ...

  8. 19年8月 字母哥 第三章 spring boot 配置原理实战 用热点公司网不行

    第三章 spring boot 配置原理实战 3.1.结合配置加载讲解bean自动装配原理 3.2.详解YAML语法及占位符语法 3.3.获取自定义配置的两种实现方法 3.4.配置文件注入值数据校验 ...

  9. SpringBoot学习笔记-2:第二章 Spring Boot 配置

    第二章 Spring Boot 配置 1.YAML 配置 SpringBoot 全局配置文件 application.properties application.yml YAML 以数据为中心,比 ...

最新文章

  1. 报告 | 数字孪生城市研究报告(2019年)
  2. C++,那些可爱的小陷阱(一)
  3. 人工智能是这样理解真正的活过了
  4. axure rp制作输入法_Axure教程丨制作自己的Axure元件库
  5. ccna____总结
  6. swift - 使用系统app导航
  7. java的ArrayList分析
  8. 如何使用一套键盘鼠标,同时控制多台电脑?
  9. devops学习(三) K8环境部署jenkins
  10. 双显卡(Intel+Nvidia)笔记本配置cuda开发环境
  11. Unity + PicoVr 360全景视频播放
  12. python存储-Python数据存储之 h5py详解
  13. 城市信息化重要载体“无线城市”
  14. STL和泛型编程_学习笔记01
  15. python计算圆柱体积_python计算圆周长、面积、球体体积并画出圆
  16. 如何修改mc服务器密码是什么意思,mc服务器密码设置密码
  17. 谈一谈打造高效能技术团队的七个方法
  18. 集种子搜索器实用工具-P2P种子搜索器提供下载
  19. 泛微OA 注册自定义接口格式
  20. Java中的相除(/)和取余(%)

热门文章

  1. Serializable接口分析
  2. TP5生成二维码教程
  3. Word文档排版——自动编号
  4. 仪表板 ajax实例,CQD 开发示例 - Skype for Business Server 2015 | Microsoft Docs
  5. 嵌入式系统开发的架构和应用
  6. 学python为何不好找工作呢?
  7. 移动端性能专项测试之 CPU
  8. 认识机器学习与深度学习
  9. 软件缺陷定位☞西门子数据集介绍
  10. 《输赢》精彩段落总结