文章目录
ORM
Object Relational Mapping 对象关系映射
创建Java类的对象,将其属性和数据库表中的字段名之间构建映射关系。
这样通过操作该类创建的一个对象,就能操作数据库中对应的数据表。
ORM框架就是对象关系映射框架,用于建华JDBC。
主流的ORM框架有Hibernate、JPA、MyBatis、MyBatisPlus等
Mybatis
一个半自动化的ORM框架。原本叫做ibatis,后来升级改名为MyBatis。
MyBatis免去了几乎所有的JDBC代码以及设置参数和获取结果集的工作
使用XML文件或注解的形式,将SQL语句映射为持久层(dao层)中的方法
官网https://mybatis.org/mybatis-3/zh/index.html
特点
- 方便
- 建华JDBC
- 解除SQL语句和业务代码的耦合
- sql和java代码分离,sql写在xml文件中,方便拓展维护。
- 支持动态SQL
- 不同的情况下,构造不同的SQL
- 主流
- SSM中的M
SSM项目搭建
整体流程
- 1.创建基于Maven的webapp项目
- 2.修改web.xml版本为4.0,创建java、resources目录、项目包结构、web-inf下的页面目录
- 3.导入依赖
- spring-webmvc
- mybatis
- mybatis-spring
- mysql
- druid
- spring-jdbc
- jstl
- 4.配置Spring
- 在resources目录下创建application.xml
- 配置web.xml
- 设置全局参数: ,读取application.xml文件
- 设置全局监听器:ContextLoaderListener,初始化Spring容器
- 5.配置SpringMVC
- 创建springmvc.xml
- 设置内部资源视图解析器:InternalResourceViewResolver,设置前后缀
- 配置web.xml
- 设置请求分发器:DispatcherServlet,在其中读取springmvc.xml配置文件
- 使用过滤器解决请求中文乱码
- 创建springmvc.xml
- 6.配置MyBatis
- 创建mybatis-config.xml(官网模板)
- 在application.xml中注入
- 数据库连接池Druid:DruidDataSource
- SQL会话工厂:SQLSessionFactory
- 映射扫描配置器:MapperScannerConfigurer
- 7.执行sql语句
- 创建dao层接口,定义方法
- 在resources下创建mapper目录,创建sql映射文件(官网模板)
具体步骤
1.创建基于Maven的webapp项目
2.修改web.xml版本为4.0,创建java、resources目录、项目包结构、web-inf下的页面目录
3.导入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.23</version>
</dependency>
<!--MySQL-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<!--Spring集成JDBC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.23</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<!--Spring集成MyBatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<!--数据库连接池Druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
4.配置Spring
- 在resources目录下创建application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描使用了Spring注解的根包-->
<context:component-scan base-package="ssm01"></context:component-scan>
</beans>
- 配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--通过全局监听器初始化Spring容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--定义全局参数读取Spring配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</context-param>
</web-app>
5.配置SpringMVC
- 在resoureces下创建springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描使用了SpringMVC注解的根包-->
<context:component-scan base-package="ssm01.controller"></context:component-scan>
<!--设置页面前缀和后缀-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
- 在web.xml中配置DispatcherServlet和中文乱码过滤器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--通过全局监听器初始化Spring容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--定义全局参数读取Spring配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</context-param>
<!--SpringMVC-->
<!--配置DispatcherServlet-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--初始化参数读取SpringMVC配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--过滤器解决中文乱码-->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
6.配置MyBatis
- 在resoureces下创建mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--设置-->
<settings>
<!--开启驼峰命名设置-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启SQL日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
- 在application.xml中注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描使用了Spring注解的根包-->
<context:component-scan base-package="ssm01.controller"></context:component-scan>
<!--注入Druid数据源-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/gamedbb?serverTimezone=Asia/Shanghai"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<!--注入MyBatis核心对象SqlSessionFactoryBean-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<!--设置数据源-->
<property name="dataSource" ref="dataSource"></property>
<!--设置MyBatis配置文件路径-->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!--设置MyBatis的SQL映射文件路径-->
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
</bean>
<!--扫描项目中的dao层目录-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--设置MyBatis核心对象SQLSessionFactoryBean的名字-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<!--dao层根包-->
<property name="basePackage" value="ssm01.dao"></property>
</bean>
</beans>
7.执行SQL
表
实体类
package ssm01.entity;
public class Hero {
private int id;
private String name;
private String position;
private String sex;
private int jiage;
private String shelf_date;
//省略get/set
}
dao层接口
package ssm01.dao;
import org.springframework.stereotype.Repository;
import ssm01.entity.Hero;
import java.util.List;
@Repository
/*使用MyBatis时,数据访问层,只需定义为接口*/
public interface HeroDao {
/*查询所有*/
List<Hero> queryAll();
}
service层
package ssm01.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ssm01.dao.HeroDao;
import ssm01.entity.Hero;
import java.util.List;
@Service
public class HeroService {
@Autowired
private HeroDao heroDao;
public List<Hero> queryAll(){
return heroDao.queryAll();
}
}
controller层
package ssm01.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import ssm01.dao.HeroDao;
import ssm01.entity.Hero;
import ssm01.service.HeroService;
import java.util.List;
@Controller
@RequestMapping("/hero")
public class HeroController {
@Autowired
private HeroService heroService;
@RequestMapping("/queryAll")
public String queryAll(Model model){
List<Hero> list = heroService.queryAll();
System.out.println(list);
model.addAttribute("list",list);
return "heroList";
}
}
在resources下的mapper目录下,创建sql映射文件,命名通常为“实体类Mapper”,如HeroMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace是该映射文件对应的dao层接口全限定名-->
<mapper namespace="ssm01.dao.HeroDao">
<!--查询select标签-->
<!--id对应dao层中的某个方法名-->
<!--如果实体类中的属性名和表中的字段名一致,加入resultType属性,设置实体的全限定名-->
<!--<select id="queryAll" resultType="ssm01.entity.Hero">
select * from hero
</select>-->
<!--resultMap标签用于给指定的实体类设置返回结果集映射-->
<!--id属性自定义该映射名称-->
<!--type属性指定实体类-->
<resultMap id="heroMap" type="ssm01.entity.Hero">
<!--主键字段用id标签,其他字段用result标签-->
<!--id和result标签都有两个属性 property表示实体类中的属性名 column表示表中的字段名-->
<id property="id" column="hid"></id>
<result column="price" property="jiage"></result>
<!--如果遇到下划线转驼峰命名,无需在这里自定义映射,可以在mybatis的配置文件中设置开启驼峰命名映射-->
<!--<result column="shelfDate" property="shelf_date"></result>-->
</resultMap>
<!--如果实体中的属性和表中的字段名不一致,使用resultMap属性设置自定义的结果集映射-->
<select id="queryAll" resultMap="heroMap">
select * from hero
</select>
</mapper>
jsp页面
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<table>
<c:forEach items="${list}" var="hero">
<tr>
<td>${hero.id}</td>
<td>${hero.name}</td>
<td>${hero.position}</td>
<td>${hero.sex}</td>
<td>${hero.jiage}</td>
<td>${hero.shelf_date}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
SSM项目搭建补充
通过db.properties文件保存连接数据库的信息
.properties文件称为属性文件,其中的数据以键值对(key=value)的形式保存
-
在resources目录下新建db.properties文件
DB_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver DB_URL=jdbc:mysql://localhost:3306/bookdbb?serverTimezone=Asia/Shanghai DB_USERNAME=root DB_PASSWORD=123456
-
在application.xml中读取properties文件
<!--读取properties文件--> <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
-
读取时使用EL表达式访问其中的键
<!--注入Druid数据源DruidDataSource--> <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource"> <property name="driverClassName" value="${DB_DRIVER_CLASS_NAME}"></property> <property name="url" value="${DB_URL}"></property> <property name="username" value="${DB_USERNAME}"></property> <property name="password" value="${DB_PASSWORD}"></property> </bean>
MyBatis配置文件常用设置
<?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>
<!--开启驼峰命名映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启SQL日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
使用BootStrap渲染数据
下载bootstrap的文件夹和jquery,保存到webapp根目录下。
由于在web.xml中,SpringMVC的核心类DispatcherServlet(请求分发器)的映射设置成了“/”,表示所有请求,包含静态资源的请求都会交给SpringMVC处理。
解决无法引入静态资源的问题
-
在webapp目录下,新建一个目录,通常命名为static。将项目中的静态资源文件都保存于此。
-
在springmvc.xml中
<!--映射静态资源目录--> <!--location表示要映射的静态资源目录--> <!--mapping表示最终通过哪种方式进行访问。这里表示只要以/static开头的请求,都可以访问静态资源目录--> <mvc:resourcesmapping="/static/**"location="/static/"></mvc:resources> <!--开启注解驱动,有这个标签,SpringMVC就能区分哪个是资源文件,哪个是Controller--> <mvc:annotation-driven></mvc:annotation-driven>
导入BootStrap的样式和JS文件
<%--导入bootstrap的css文件--%>
<linkhref="${pageContext.request.contextPath}/static/bootstrap-3.4.1-
dist/css/bootstrap.min.css"rel="stylesheet"
type="text/css">
<%--导入jquery--%>
<scriptsrc="${pageContext.request.contextPath}/static/bootstrap-3.4.1-dist/js/jquery-3.6.2.min.js"></script>
<%--导入boostrap的js文件--%>
<scriptsrc="${pageContext.request.contextPath}/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
在SpringMVC中,让某个控制层中的方法返回JSON格式的数据
-
添加依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.2</version> </dependency>
-
@ResponseBody注释
该注解可以加在类或方法上
- 如果加在方法上,表示该方法的返回值类型为JSON格式
- 如果加在类上,表示该类中的所有方法的返回值类型为JSON格式
package SSM02.controller; import SSM02.entity.BookType; import SSM02.service.BookTypeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; @Controller @RequestMapping("/bookType") public class BookTypeController { @Autowired private BookTypeService bookTypeService; @RequestMapping("/queryAll") public String queryAll(){ List<BookType> bookTypes = bookTypeService.queryAll(); System.out.println(bookTypes); return ""; } //如果controller的某个方法返回一个json字符串 @RequestMapping("/queryAllToJson") @ResponseBody//该方法无论返回值是什么,浏览器都能解析为JSON格式字符串 public List<BookType> queryAllToJson(){ List<BookType> list = bookTypeService.queryAll(); return list; } }
SSM项目中使用Ajax
ajax依赖于jquery,所以先保证页面中存在jquery.js。
<script>
/*
*使用ajax查询所有图书类型
* 在页面中使用ajax访问后端如果要得到数据,该数据必须是JSON格式
* */
$.ajax({
url:"${pageContext.request.contextPath}/bookType/queryAllToJson",
success:function (res){
//这里的res需要所有的类型集合的JSON字符串
// console.log(res)
for (var i=0;i<res.length;i++){
var $opt = $("<option value='" + res[i].typeId+"'>" + res[i].typeName + "</option>")
$("select[name=typeId]").append($opt);
}
}
})
</script>
在前端页面中使用ajax访问controller时,controller的返回值必须是一个JSON格式的字符串。
所以controller中的方法上要加入@ResponseBody注解
单元测试JUnit
1.导入依赖
如果只在普通的Maven项目中使用,只需导入该依赖
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
如果要在spring的Maven项目中使用,还需要导入一下依赖
<!--spring集成Junit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.23</version>
<scope>test</scope>
</dependency>
2.创建单元测试类所在目录
在项目的src目录下新建test\java目录。
3.使用
-
在test/java目录下创建测试类
-
在类上加入
- @ContextConfiguration(“classpath:application.xml”)
- @RunWith(SpringJUnit4ClassRunner.class)
-
在类中创建公开的无返回值无参数方法,加入@Test注解
-
在该方法上右键运行,运行的是当前方法,在类中空白处右键运行,运行的是当前类中的所有测试方法
import SSM02.dao.BookInfoDao; import org.apache.ibatis.logging.stdout.StdOutImpl; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; //解析Spring配置文件 @ContextConfiguration("classpath:application.xml") @RunWith(SpringJUnit4ClassRunner.class) public class Test { /*单元测试的方法必须是无参数无返回值的公共方法*/ @org.junit.Test public void fun(){ System.out.println("单元测试"); } @Autowired private BookInfoDao bookInfoDao; @org.junit.Test public void test1(){ bookInfoDao.queryAll(); } }
MyBatis整理
使用前提
-
搭建好MyBatis的环境
-
mybatis配置文件模板
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> </configuration>
-
-
根据表创建实体类
- 默认属性要和字段名一致
- 如果表中的字段名使用下划线分割单词,实体属性使用驼峰命名法,在mybatis的配置文件中加入开启驼峰命名方式映射
<!--设置--> <settings> <!--开启驼峰命名设置--> <setting name="mapUnderscoreToCamelCase" value="true"/> <!--开启SQL日志--> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
-
如果表中字段名和实体的属性名既不一致,也不是驼峰命名,就要在写SQL语句时,指定字段名和属性名的映射关系
-
实体类必须是公共的,其中必须有无参数的构造方法和get/set方法
-
数据访问层
- 数据访问层通常是接口(只需要定义方法名和返回值),可以称为dao层或mapper层,命名时可以是xxDao或xxMapper
-
写接口的SQL映射文件
- 命名通常为xxxMapper.xml
- SQL映射文件模板
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="数据访问层接口的全限定名"> </mapper>
-
SQL映射文件可以放在resources目录下,spring的配置文件application.xml通过“classpath:具体路径”读取
如保存在resources/mapper下
<!--注入MyBatis核心对象SqlSessionFactoryBean--> <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory"> <!--设置数据源(引用类型用ref)--> <property name="dataSource" ref="dataSource"></property> <!--设置MyBatis配置文件路径--> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <!--设置MyBatis的SQL映射文件路径--> <property name="mapperLocations" value="classpath:mapper/*.xml"></property> </bean>
-
映射文件也可能放在数据访问层目录下,即dao包下
IDEA中,如果将xml文件保存在java包下是,默认在target目录(真正运行时的目录)不会加载这些文件,要进行设置,在pom.xml中的build标签中加入以下内容,重新加载
<!--解决xml如果放在java包下时无法编译的问题--> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources>
spring的配置文件application.xml中读取路径正确
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory"> <!--设置数据源(引用类型用ref)--> <property name="dataSource" ref="dataSource"></property> <!--设置MyBatis配置文件路径--> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <!--设置MyBatis的SQL映射文件路径--> <property name="mapperLocations" value="classpath:SSM02/dao/mapper/*.xml"></property> </bean>
查询<select></select>
单表查询
dao中的方法
List<BookType> queryAll();
sql映射文件
-
当数据库表中的字段名和实体属性一致或开启了驼峰命名映射
<select id="queryAll" resultType="com.xxx.entity.BookType"> select * from book_type </select>
-
当数据库表中的字段名和实体属性不一致,使用resultMap
<select id="queryAll" resultMap="map"> select * from book_type </select> <resultMap id="map" type="com.xxx.entity.BookType"> <!--主键使用id标签,其它字段使用result标签--> <!--column对应字段名,property对应属性名--> <id column="type_id" property="typeId"></id> <id column="type_name" property="typeName"></id> </resultMap>
多表查询
多对一查询
多对一表示多个从表实体对应一个主表实体,如多本图书对应一个图书类型。
在从表实体中,额外添加一个属性:对应的主表的实体对象
public class BookInfo {
private int bookId;
private int typeId;
private String bookName;
private String bookAuthor;
private int bookPrice;
private int bookNum;
private String publisherDate;
private String img;
//多对一查询,额外添加外键对应的实体对象
private BookType bookType;
}
dao中的方法
List<BookInfo> queryAll();
sql映射文件
-
关联查询:构建特殊的SQL语句
- 适合外键对应的表中字段比较少的情况下使用
<!--sql语句除了查询自身表之外,还要查询关联表中的字段,将其命名为“实体对象.属性”--> <select id="queryAll" resultType="SSM02.entity.BookInfo"> select bi.*,type_name as 'bookType.typeName' from book_info bi,book_type bt where bi.type_id=bt.type_id </select>
-
连接查询(不建议)
<!--外键关联查询。方式二:关联查询--> <select id="queryAll" resultMap="booksMap"> select * from book_info bi, book_type bt where bi.type_id = bt.type_id </select> <resultMap id="booksMap" type="SSM02.entity.BookInfo"> <!--BookInfo中的属性不会自动赋值--> <id property="bookId" column="book_id"></id> <result property="bookName" column="book_name"></result> <result property="typeId" column="type_id"></result> <result property="bookAuthor" column="book_author"></result> <result property="bookPrice" column="book_price"></result> <result property="bookNum" column="book_num"></result> <result property="img" column="img"></result> <result property="publisherDate" column="publish_date"></result> <!--外键对应的实体对象,使用association标签,通过property属性对应实体对象--> <association property="bookType"> <id column="type_id" property="typeId"></id> <result column="type_name" property="typeName"></result> </association> </resultMap>
-
子查询
- 适合外键对应的表字段比较多的情况
<!--多表查询,方式三:子查询--> <!--1.查询从表--> <select id="queryAll" resultMap="booksMap"> select * from book_info </select> <!--2.自定义结果映射,使用type_id进行子查询,执行下方的sql--> <resultMap id="booksMap" type="SSM02.entity.BookInfo"> <!--由于使用type_id进行了子查询,所以如果要给type_id赋值,需要再次映射一次--> <result column="type_id" property="typeId"></result> <!--外键对应的实体对象,使用association赋值--> <!--如果这里的子查询来自当前映射文件--> <association property="bookType" column="type_id" select="findTypeByTypeId"></association> <!--如果这里的子查询来自其他dao中的方法--> <!--<association property="bookType" column="type_id" select="SSM02.dao.BookTypeDao.findById"></association>--> </resultMap> <!--3.根据type_id查询完整对象--> <select id="findTypeByTypeId" resultType="SSM02.entity.BookType"> select * from book_type where type_id=#{typeId} </select>
一对多查询
一对多表示一个主表实体对应多个从表实体。如一个图书类型对应多本图书。
在主表对应的实体类中,额外添加一个属性:多个从表对象的集合
public class BookType {
private int typeId;
private String typeName;
//一对多查询,额外添加一个属性,该类型下的对象集合
List<BookInfo> books;
}
dao中的方法
BookType queryBooksByType(int typeId);
sql映射文件
-
连接查询
<!--一对多查询,方式一:连接查询--> <select id="queryBooksByType" resultMap="testMap"> select * from book_info bi inner join book_type bt on bi.type_id = bt.type_id where bi.type_id = #{typeId} </select> <!--自定义结果映射集--> <resultMap id="testMap" type="SSM02.entity.BookType"> <id property="typeId" column="type_id"></id> <result column="type_name" property="typeName"></result> <!--集合(List)类型的属性,使用collection标签,使用ofType设置集合中的对象类型--> <collection property="books" ofType="SSM02.entity.BookInfo"> <id column="book_id" property="bookId"></id> <result column="book_name" property="bookName"></result> <result column="type_id" property="typeId"></result> <result column="book_author" property="bookAuthor"></result> <result column="book_num" property="bookNum"></result> <result column="book_price" property="bookPrice"></result> <result column="img" property="img"></result> <result column="publisher_date" property="publisherDate"></result> </collection> </resultMap>
-
子查询
<!--一对多查询,方式二:子查询--> <!--1.根据类型编号查询自身表--> <select id="queryBooksByType" resultMap="testMap"> select * from book_type where type_id = #{typeId} </select> <!--2.设置结果集映射--> <resultMap id="testMap" type="SSM02.entity.BookType"> <id column="type_id" property="typeId"></id> <result column="type_name" property="typeName"></result> <!--集合对象,使用collection标签。使用type_id字段执行子查询getBooksByTypeId,将结果映射色到books属性--> <collection property="books" column="type_id" select="getBooksByTypeId"></collection> </resultMap> <!--3.子查询,根据类型编号查询所有对应的图书集合--> <select id="getBooksByTypeId" resultType="SSM02.entity.BookInfo"> select * from book_info where type_id = #{typeId} </select>
总结
-
多对一
如果查询要以从表数据为主,关联主表相应的数据时,属于多对一查询。如查询所有图书的同时,显示其类型。建议使用子查询或自定义特殊的sql语句。都需要在从表实体中添加一个主表对象属性。
- 主表字段比较少,建议使用自定义特殊的sql语句,保证字段重命名为"主表对象.属性"
- 主表字段比较多,建议使用子查询,使用标签映射主表对象属性
-
一对多
如果查询要以主表数据为主,关联该主键对应的从表实体对象的集合时,属于一对多查询。如查询某个类型,同时显示该类型下的所有图书。建议使用子查询。在主表实体中添加一个从表集合属性。使用**<collection>**标签映射从表集合属性
多条件查询
参考#{}和${}的用法
#{}和${}
在mybatis的sql映射文件中,可以使用#{}和${}表示sql语句中的参数。
#{}相当于预处理,会将参数用?占位后传值
${}相当于拼接,会将参数原样拼接
通常使用#{},防止sql注入
--#{}会自动用''将参数引起来,如参数为admin
select * from user where username=#{username}
select * from user where username='admin'
--${}会将参数原样拼接,如参数为admin
select * from user where username=${username}
select * from user where username=admin
${}使用情况
如有批量删除的sql,需要传递" 1,2,3,4,5"参数
delete from user where id in(1,2,3,4,5)
int delelteByIds(String ids);
//如实际传递的是"1,2,3,4,5"
<delete id="deleteByIds">
delete from user where id in (${ids})
</delete>
#{}的使用
删除<delete></delete>
删除单个
delete from 表 where 字段 = 值
值通常为主键id
删除多个
delete from 表 where 字段 in (数据集合)
如delete from book_info where book_id in (100,101,102)
in后面的内容通常是一个数组
前端页面通过复选框选择要删除的数据,获取选中的数据的id,保存到一个数组中
dao层接口
int deleteByIds(@param("idList")List<Integer> idList)
sql映射文件
<delete id="deleteByIds">
delete from book_info where book_id in
<!--
foreach标签用户遍历dao层传递的集合对象
collection表示要遍历的集合
item表示遍历出的每个对象
separator表示将遍历出的数据用什么分隔
open和close表示将遍历出的数据使用什么开头和结尾
-->
<foreach collection="idList" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
添加<insert></insert>
添加时的参数虽多,但通常为一个完整的实体对象,所以dao层接口中方法的参数要定义成一个实体对象。
dao层
int insert(BookType bookType)
sql映射文件
- 通常加上parameterType参数指定添加的对象实体类全限定名
- #{}中的参数一定来自于dao层接口方法实体参数对象的属性
- 如果要在添加成功的同时,获取自动增长的主键值时,要添加
- useGeneratedKeys="true"表示启动自动获取自增的值
- keyColumn="type_id"表示自增字段
- keyProperty="typeId"表示实体类中的主键属性
<insert id="testInsert" useGeneratedKeys="true" keyColumn="type_id" keyProperty="typeId" parameterType="SSM02.entity.BookType">
insert into book_type values(null,#{typeName})
</insert>
修改<update></update>
修改可以分为修改所有字段和修改部分字段
修改所有字段
dao层
int update(BookInfo bookInfo);
sql映射文件
<update id="update">
update book_info set
book_name=#{bookName},
book_author=#{bookAuthor},
book_num=#{bookNum},
book_price=#{bookPrice},
publisher_date=#{publisherDate}
where book_id=#{bookId}
</update>
这种方式会修改所有字段,如果实体参数的某个属性没有赋值,就会使用该属性的默认值进行修改
如String类型的属性没有赋值时,就会用null修改,int类型用0修改,如果表汇总该字段非空,就会导致SQL执行异常。
所以修改所有字段,参数为一个完整对象时,保证其中的属性都有值
修改部分字段
方式一
dao层
int updateSth(int bookId,int bookPrice,int bookNum);
sql映射文件
<update id="updateSth">
update book_info set
book_num = #{bookNum},
book_price = #{bookPrice}
where book_id = #{bookId}
</update>
方式二
dao层
int updateSth(BookInfo bookInfo);
sql映射文件
<update id="updateSth">
update book_info set
<if test="bookName!=null">
book_num = #{bookNum},
</if>
<if test="bookPrice!=null">
book_price = #{bookPrice}
</if>
where book_id = #{bookId}
</update>
这样写,有以下几个问题
1.如果只有第一个条件满足,最终拼接的SQL语句,会在所有条件后,where关键字之前多出一个逗号,导致语法错误
解决方式:将set替换成<set></set>
标签,mybatis会自动去除最后的逗号:
<update id="updateSth">
update book_info
<set>
<if test="bookName!=null">
book_num = #{bookNum},
</if>
<if test="bookPrice!=null">
book_price = #{bookPrice}
</if>
</set>
where book_id = #{bookId}
</update>
2.替换为<set>
标签后,如果所有提哦啊见都不满足,mybatis会自动去除set部分,SQL语句就会变成update book_info where book_id = ?,导致语法错误
解决方式:在<set>
标签中,添加一个不影响原始数据的条件,如<if test="bookId=bookId">book_id=#{bookId}</if>
只需要一个book_id参数,sql语句没有语法错误,对原始数没有任何影响
动态SQL语句
<set>
搭配<if>
用于修改
<update id="testUpdate">
update book_info
<set>
<if test="bookId=bookId">
book_id=#{bookId},
</if>
<if test="bookName!=null">
book_num = #{bookNum},
</if>
<if test="bookPrice!=null">
book_price = #{bookPrice}
</if>
</set>
where book_id=#{bookId}
</update>
<where>
搭配<if>
用于查询、修改、删除时的条件
<select id="queryByCondition" resultType="com.xxx.entity.BookInfo">
select * from book_info
<where>
<if test="bookName!=null">
book_name=#{bookName}
</if>
<if test="bookAuthor!=null">
book_author=#{bookAuthor}
</if>
<if test="typeId!=null">
and type_id=#{typeId}
</if>
</where>
</select>
-
<trim>
可以替换self-if和where-if该标签有四个属性
prefix 表示如果trim标签中有if条件满足,就在整个trim部分添加指定前缀
suffix 表示如果trim标签中有if条件满足,就在整个trim部分添加指定后缀
prefexOverrides 表示去除整个trim部分多余的前缀
suffixOverrides 表示去除整个trim部分多余的前缀
使用trim实现修改
可以修改所有字段,也可以修改部分字段
<update id="testUpdate">
update book_info
<trim prefix="set" suffixOverrides=",">
book_id=#{bookId},
<if test="bookName!=null">
book_name=#{bookName},
</if>
<if test="bookAuthor!=null">
book_author=#{bookAuthor},
</if>
<if test="bookPrice!=null">
book_price=#{bookPrice},
</if>
<if test="bookNum!=null">
book_num=#{bookNum},
</if>
<if test="publisherDate!=null">
publisher_date=#{publisherDate}
</if>
</trim>
where book_id=#{bookId}
</update>
使用trim标签实现多条件查询
<select id="queryByCondition" resultType="SSM02.entity.BookInfo">
select bi.*,type_name as 'bookType.typeName'
from book_info bi,book_type bt
<trim prefix="where" prefixOverrides="and" suffix="order by book_id asc">
bi.type_id=bt.type_id
<if test="bookName!=null">
and book_name like concat ('%',#{bookName},'%')
</if>
<if test="bookAuthor!=null">
and book_author like concat ('%',#{bookAuthor},'%')
</if>
<if test="typeId!=0">
and bt.type_id = #{typeId}
</if>
</trim>
</select>