文章目录
1、Spring
1.1 Spring 简介
我们一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发
相关组件
- Spring Core: 基础,可以说 Spring 其他所有的功能都需要依赖于该类库。主要提供 IoC 依赖注入功能。
- Spring Aspects : 该模块为与 AspectJ 的集成提供支持。
- Spring AOP :提供了面向切面的编程实现。
- Spring JDBC : Java 数据库连接。
- Spring JMS :Java 消息服务。
- Spring ORM : 用于支持 Hibernate 等 ORM 工具。
- Spring Web : 为创建 Web 应用程序提供支持。
- Spring Test : 提供了对 JUnit 和 TestNG 测试的支持。
总结来说:Spring 是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。
需要主要掌握 Spring 四个方面的功能:
- IoC / DI
- AOP
- 事务
- JDBCTemplate
1.2 IOC 简介
IoC(Inverse of Control:控制反转)是一种设计思想,就是 将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
IoC 在其他语言中也有应用,并非 Spring 特有。 IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。
将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。 在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。、
总结:程序员创建对象从主动创建对象到被动创建对象的改变就叫做控制反转 IoC。此处的被动创建就是从IOC容器中取想要的对象的Bean(如何取:从注册该对象的Bean的xml配置文件中取)IoC 只能解决程序间的依赖关系,别的事情都干不了
注:IOC相当于一个对象工厂,不同于简单工厂和静态工厂的通过类方法写好后再注入到Spring容器中,IOC是通过xml配置中注册好的Bean来创建对象。
1.3 Bean
1.3.1 Bean的获取
我们通过ac.getBean
方法来从 Spring 容器中获取 Bean,传入的参数是 Bean 的 name 或者 id 属性。除了这种方式,也可以直接通过 Class 去获取一个 Bean。
public class Client {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService aService = ac.getBean(IAccountService.class);
System.out.println(aService);
}
}
这种方式存在一个很大的弊端,如果存在多个实例(多个 Bean),这种方式就不可用。
所以一般建议通过 name 或者 id 去获取 Bean 的实例
1.3.2 Bean的创建
1.3.2.1 XML配置(三种方法)
第一种:使用默认无参构造函数
在 Spring 的配置文件中使用 bean 标签,配以 id 和 class 属性后,且没有其他属性和标签时。采用的就是默认构造函数创建 bean 对象,此时如果 bean(类) 中没有默认无参构造函数,将会创建失败
<bean id = "accountService" class = "com.zxg.service.impl.AccountServiceImpl">
第二种:使用简单工厂模式的方法创建(使用某个类中的方法创建对象,并存入 Spring 容器)
/**
* 模拟一个工厂类
* 该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数
* 此工厂创建对象,必须先有工厂实例对象,再调用方法
*/
public class InstanceFactory {
public IAccountService createAccountService(){
return new AccountServiceImpl();
}
}
<bean id = "InstanceFactory" class = "com.zxg.factory.InstanceFactory"></bean>
<bean id="accountService"
factory-bean="InstanceFactory"
factory-method="createAccountService">
</bean>
第三种:使用静态工厂的方法创建对象(使用某个类中的静态方法创建对象,并存入 Spring 容器)
/**
* 模拟一个静态工厂类
* 该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数
*/
public class StaticFactory {
public static IAccountService createAccountService(){
return new AccountServiceImpl();
}
}
<bean id="accountService"
class="com.zxg.factory.StaticFactory"
factory-method="createAccountService">
</bean>
1.3.2.2 注解配置
以下注解的作用和在 XML 配置文件中编写一个 bean 标签实现的功能是一样的 , 用于把当前类对象存入 Spring 容器中
使用以下注解的时候,需要在 xml 文件中配置如下:(当然,其他的 bean 注册配置就不用写了,配合下面注解这一行就可以了)
<!--告知Spirng在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为context空间和约束中-->
<context:component-scan base-package="com.zxg"></context:component-scan>
-
@Component
value属性 : 用于指定 bean 的 id 。当我们不写时,他的默认值是当前类名,且首字母小写。
-
@Controller
: 一般用于表现层的注解。 -
@Service
: 一般用于业务层的注解。 -
@Repository
: 一般用于持久层的注解。
上述四个注解可以随意互换, 作用相同, 都是用于用于把当前类对象存入 Spring 容器中, 只不过后面三个注解提供了更具体的语义化罢了.
// 没有写 value 默认值 'accountServiceImpl'
@Service
public class AccountServiceImpl implements IAccountService {
// doSomething
}
1.3.3 Bean的作用范围
从 Spring 容器中多次获取同一个Bean,默认情况下,获取到的实际上是同一个实例,即默认是单例的。当然,我们可以手动配置
1.3.3.1 XML配置
<bean class = "com.zxg.dao.useDaoImpl" id = "userDao" scope = "prototype"/>
bean 标签的 scope
属性就是用来指定 bean 的作用范围的
- singleton : 默认值,单例的. (bean对象默认是单例模式)
- prototype : 多例的.
- request : 作用于web应用的请求范围。WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
- session : 作用于web应用的会话范围。WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
- global-session :作用于集群环境的会话范围。WEB 项目中,应用在 Portlet(集群) 环境.如果没有 Portlet 环境那么 globalSession 相当于 session.
1.3.3.2 注解配置
当然,除了使用 bean 标签在 xml 中进行配置,我们也可以在 Java 代码中使用注解 @Scope
来配置Bean的作用范围
@Repository
@Scope("prototype")
public calss UserDao{
public String hello(){
return "hello";
}
}
1.3.4 Bean的生命周期
单例对象:scope="singleton"
一个应用只有一个对象的实例。它的作用范围就是整个引用。
生命周期:
- 对象出生:当应用加载,创建容器时,对象就被创建了。
- 对象活着:只要容器在,对象一直活着。
- 对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
总结: 单例对象的生命周期和容器相同
多例对象:scope="prototype"
每次访问对象时,都会重新创建对象实例。
生命周期:
- 对象出生:当使用对象时,才会创建新的对象实例。
- 对象活着:只要对象在使用中,就一直活着。
- 对象死亡:当对象长时间不用,且没有别的对象引用时,由 java 的垃圾回收器进行回收。
1.4 依赖注入(DI)
依赖注入:Dependency Injection
。它是 Spring 框架核心 IoC 的具体实现。
我们的程序在编写时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。 IoC 解耦只是降低他们的依赖关系,但不会消除。
例如:我们的业务层仍会调用持久层的方法。 那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
能注入的数据有三类:
- 基本类型和 String
- 其他 bean 类型(在配置文件中或者注解配置过的bean)
- 复杂类型/集合类型
注入的方式有三种:
- 构造函数
- set方法
- 注解
1.4.1 Xml 配置
1.4.1.1 构造函数注入
顾名思义,就是使用类中的有参构造函数,给成员变量赋值
-
构造函数注入:
使用的便签:
constructor-arg
标签出现的位置:bean标签的内部
-
标签中的属性:
index
:指定要注入的数据在构造函数参数列表的索引位置 ,从0开始type
: 用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型name
:用于给构造函数中指定名称的参数赋值- value : 它能赋的值是基本数据类型和 String 类型
- ref : 它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中或者注解中配置过的 bean
示例代码:
public class AccountServiceImpl implements IAccountService {
private String name;
private Integer age;
private Date birthday;
public AccountServiceImpl(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
@Override
public void saveAccount() {
System.out.println(name+","+age+","+birthday);
}
}
<bean id = "accountService" class = "com.zxg.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name = "age" value="20"></constructor-arg>
<constructor-arg name = "birthday" ref="now"></constructor-arg>
</bean>
<!--配置一个日期对象
读取这个类名通过反射创建对象并存入spring容器中,我们可以通过id来使用它
-->
<bean id="now" class="java.util.Date"></bean>
此处的 value 也可以在 classpath 目录下新建一个 properties 文件,利用 SPEL 表达式取值,比如:
<bean id = "accountService" class = "com.zxg.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="${name}"></constructor-arg>
<constructor-arg name = "age" value="${age}"></constructor-arg>
</bean>
我们在创建对象时,即使用不到这些数据时,也得给他们都赋值
1.4.1.2 set 方法注入
顾名思义,就是在类中提供需要注入成员的 set 方法
- 涉及的标签:
property
- 出现的位置:bean标签的内部
- 标签的属性:
name
: 指定注入时所调用的set方法名称value
: 它能赋的值是基本数据类型和 String 类型ref
:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中或者注解中配置过的 bean
示例代码:
public class AccountServiceImpl implements IAccountService {
private String name;
private Integer age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public void saveAccount() {
System.out.println(name+","+age+","+birthday);
}
}
<bean id = "accountService" class = "com.zxg.service.impl.AccountServiceImpl">
<property name="name" value="test"></property>
<property name="age" value="20"></property>
<property name="birthday" ref = "now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>
set 注入的优势:创建对象时没有明确的限制,可以直接使用默认构造函数
1.4.1.3 集合类型的注入(本质还是set)
用于给list结构集合注入数据的标签:list、array、set
用于给Map结构集合注入数据的标签 : map、props
结构相同,标签可以互换
示例代码:
public class AccountServiceImpl implements IAccountService {
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myProps;
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMyProps(Properties myProps) {
this.myProps = myProps;
}
@Override
public void saveAccount() {
System.out.println(Arrays.toString(myStrs));
System.out.println(myList);
System.out.println(mySet);
System.out.println(myMap);
System.out.println(myProps);
}
}
<bean id = "accountService" class = "com.smallbeef.service.impl.AccountServiceImpl">
<property name="myStrs">
<array>
<value>A</value>
<value>B</value>
<value>C</value>
</array>
</property>
<property name="myList">
<list>
<value>A</value>
<value>B</value>
<value>C</value>
</list>
</property>
<property name="mySet">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="testA" value="A"></entry>
<entry key="testB">
<value>B</value>
</entry>
</map>
</property>
<property name="myProps">
<props>
<prop key="testC">C</prop>
<prop key="testD">D</prop>
</props>
</property>
</bean>
1.4.2 注解配置
下面注解的的作用和在 XML 配置文件的 bean 标签中编写一个 property 标签实现的功能是一样的(set方法注入)
1.4.2.1 @Autowired
作用: 自动按照类型注入。
出现位置:变量和方法上都可以
当使用注解注入属性时,set 方法可以省略。它只能注入其他 bean 类型。
在 Spring 容器查找,找到了注入成功。找不到 就报错。
当有多个类型匹配时,使用要注入的 对象变量名称 作为 bean 的 id
示例代码:
@Component
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDaoImpl;
@Override
public void saveAccount() {
accountDaoImpl.saveAccount();
}
}
-
只有一个相符合的bean时,直接匹配数据类型
-
有多个相符合的bean时,先匹配数据类型,再将变量名称和bean的id进行匹配
当变量名称找不到一样的 bean 的 id 的时候,就会报错。
为解决变量名称和 bean 的 id 不匹配的情况,有了如下注解
Qualifier
。
1.4.2.2 @Qualifier
作用: 在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。
它在给成员变量注入时不能独立使用,必须和 @Autowire
一起使用;但是给方法参数注入时,可以独立使用
属性: value:指定 bean 的 id。
示例代码:
@Component
public class AccountServiceImpl implements IAccountService {
@Autowired
@Qualifier("accountDaoImpl1")
private IAccountDao accountDaoImpl;
@Override
public void saveAccount() {
accountDaoImpl.saveAccount();
}
}
1.4.2.3 @Resource
作用: 直接按照 Bean 的 id 注入。可以独立使用(相当于Autowired + Qualifier)。它也只能注入其他 bean 类型。
属性: name:指定 bean 的 id (可不写)。
@Component
public class AccountServiceImpl implements IAccountService {
@Resource(name = "accountDaoImpl2")
private IAccountDao accountDaoImpl;
@Override
public void saveAccount() {
accountDaoImpl2.saveAccount();
}
}
以上三个注解都只能能注入其他 bean 类型的数据,而基本类型和 String 类型无法使用上述注解实现(用 @Value
实现)。
另外,集合类型的注入只能通过 XML 来实现
1.4.2.4 @Value
作用: 注入基本数据类型和 String 类型的数据 。和 依赖注入 Xml 配置中的value属性作用相同
属性: value:用于指定值。它可以使用 Spring 中 SpEL
(也就是spring中的EL表达式, ${表达式}
)
例如:
@Value("王老师")
private String name;
或者在 classpath 目录下新建一个 config.properties 文件
name = 王老师
使用 SpEl 表达式取值
@Value("${name}")
private String name;
2、SpringMVC
2.1 执行过程
注:浏览器请求->中央控制器->处理器映射器->处理器适配器->视图解析器->渲染到浏览器
SpringMVC三大核心组件:处理器映射器、处理器适配器、视图解析器
2.2 spring-mvc.xml配置
自动配置处理器映射器、处理器适配器
<!-- 开启SpringMVC对注解的支持 -->
<!--
支持mvc注解驱动
在spring中一般用@RequestMapping注解完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandLerMapping(处理器映射器)
和一个AnnotationMethodHandLerAdapter(处理器适配器)实例
这两个实例分别在类级别和方法级别处理
annotation-driven配置帮助我们自动完成上述两个实例的注入
-->
<mvc:annotation-driven/>
配置视图解析器
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
2.3 @RequestMapping 详解
@RequestMapping: 请求映射
用于建立请求 URL 和处理请求方法之间的对应关系。
若 @RequestMapping 出现在类上,则类中的所有方法的 url 都必须加上该前缀
比如 以下代码中 findAccount() 的 前端请求 url 就是 /account/findAccount
@Controller("accountController")
@RequestMapping("/account")
public class AccountController {
@RequestMapping("/findAccount")
public String findAccount() {
System.out.println("查询了账户。。。。");
return "success";
}
}
-
path
:指定请求路径的url -
value
:value属性和path属性是一样的 -
mthod
:指定该方法的请求方式 POST/GET/PUT…@RequestMapping(value="/saveAccount",method=RequestMethod.POST)
a标签的请求方式是固定不变的,是GET类型
-
params
:指定限制请求参数的条件@RequestMapping(value="/removeAccount",params= {"accountName","money>100"}) —————————————————————————————————————————— <a href="account/removeAccount?accountName=aaa&money>100">删除账户,金额 100</a> <br/> <a href="account/removeAccount?accountName=aaa&money>150">删除账户,金额 150</a>
注意:
当我们点击第一个超链接时,可以访问成功。
当我们点击第二个超链接时,无法访问。
-
headers
发送的请求中必须包含的请求头
2.4 Model 与 ModelAndView 详解
概念
执行业务逻辑,用于产生模型数据 Model ,而视图 View 用于渲染模型数据。 使用 Model 和 ModelAndView 这两个类在 Spring 的视图解析时作用以及区别:
- Model只是用来传输数据的,并不会进行业务的寻址。ModelAndView 却是可以进行业务寻址的,就是设置对应的要请求的静态文件,这里的静态文件指的是类似jsp的文件
- Model是每一次请求可以自动创建,但是ModelAndView 是需要我们自己去new的
2.4.1 Model
Model 是每次请求中都存在的默认参数,利用其 addAttribute()
方法即可将服务器的值传递到 jsp 页面中,可在 jsp 界面通过 EL 表达式获取传值
示例代码:
@RequestMapping("listCategory2")
public String listCategory2(Model model) {
// 接收查询的信息
List<Category> cs2= categoryService.list();
// 封装了查询的数据
model.addAttribute("test", cs2);
//重要!!需要给出返回model跳转的路径
return "success";
}
<!-- 获取值的时候,对应的是addAttribute的第一个参数!取了个别名为c-->
<c:forEach items="${test }" var="c" >
<tr>
<td>${c.id}</td>
<td>${c.name}</td>
</tr>
</c:forEach>
2.4.2 ModelMap
ModelMap
:ModelMap 对象主要用于传递控制方法处理数据到结果页面,也就是说我们把结果页面上需要的数据放到 ModelMap 对象中即可, 他的作用类似于 request 对象的 setAttribute 方法的作用: 用来在一个请求过程中传递处理的数据。 ModelMap 或者 Model 通过 addAttribute 方法向页面传递参数.
注:一般使用者使用modelmap.put(key,value);
2.4.3 ModelAndView
使用ModelAndView类用来存储处理完后的结果数据,以及显示该数据的视图。ModelAndView 中的Model代表模型,View代表视图,这个名字就很好地解释了该类的作用
添加模型数据 :
ModelAndView addObject(String attributeName, Object attributeValue)
ModelAndView addAllObject(Map<String, ?> modelMap)
设置视图:
void setView(View view)
void setViewName(String viewName)
示例代码:
@RequestMapping("listCategory")
public ModelAndView listCategory(){
//创建一个模型视图对象
ModelAndView mav = new ModelAndView();
//获取到查询的数据
List<Category> cs= categoryService.list();
//将数据放置到ModelAndView对象view中
mav.addObject("cs_model", cs);
// 放入jsp路径
mav.setViewName("listCategory");
//返回ModelAndView对象mav
return mav;
}
同样在 jsp 界面通过 EL 表达式获取传值
2.5 SpirngMVC中的Controller的返回值类型
2.5.1 返回ModelAndView类型
/**
* 返回ModelAndView对象
* 可以传入视图的名称(即跳转的页面),还可以传入对象。
* @return
* @throws Exception
*/
@RequestMapping(value="/findAll")
public ModelAndView findAll() throws Exception {
ModelAndView mv = new ModelAndView();
// 模拟从数据库中查询所有的用户信息
List<User> users = new ArrayList<>();
User user1 = new User();
user1.setUsername("张三");
user1.setPassword("123");
User user2 = new User();
user2.setUsername("赵四");
user2.setPassword("456");
users.add(user1);
users.add(user2);
// 添加对象
mv.addObject("users", users);
// 跳转到list.jsp的页面
mv.setViewName("list");
return mv;
}
2.5.2 返回void类型
由于默认的 Maven 项目中 没有 servlet,所以需要额外添加一个依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
-
通过
HttpServletRequest
做服务端跳转@RequstMapping public void hello(HttpServletRequest req,HttpServletResponse resp){ req.getRequestDispatcher("/hello.jsp").forward(req,resp); }
-
通过
HttpServletResponse
做重定向重定向无法访问 WEB-INF 路径下的资源
@RequstMapping public void hello(HttpServletRequest req,HttpServletResponse resp){ resp.sendRedirct(request.getContextPath()+"/hello.jsp"); }
-
通过
HttpServletResponse
给出相应这种方式,既可以返回 JSON,也可以返回普通字符串
@RequstMapping public void hello(HttpServletRequest req,HttpServletResponse resp){ resp.setContentType("text/html;charset=utf-8"); PrintWriter out = resp.getWriter(); out.write("hello"); out.flush(); out.close(); }
2.5.3 返回字符串类型
-
返回逻辑视图名
@RequestMapping public String hello(Model model){ model.addAttribute("username","xiaowu"); return "success"; //去查找一个名为 success 的视图 }
-
服务端跳转:请求转发
forward
后面跟上跳转的路径@RequestMapping public String hello(){ return "forward:/success.jsp"; }
-
客户端跳转:重定向
@RequestMapping public String hello(){ return "redirect:/success"; }
-
返回字符串
上面三个返回的字符串都是有特殊含义的,如果一定要返回一个字符串,需要额外添加一个注解
@ResponseBody
,表示当前方法的返回值就是要展示出来的返回值,没有特殊含义@RequestMapping @ResponseBody public String hello(){ return "I love China"; }
如果返回中文字符串,是会乱码的,需要在 @RequestMapping 中添加 produces 属性解决
@RequestMapping(produces = "text/html;charset = utf-8")
-
返回JSON字符串格式
@RestController //该类下的方法全返回JSON字符串
public class UserController {
@RequestMapping("/j1")
// @ResponseBody 不走视图解析器,直接返回一个字符串
public String json1() throws JsonProcessingException {
//jackson, ObjertMapper
ObjectMapper mapper = new ObjectMapper();
//创建一个User对象
User user = new User("zxg",21,"男");
String str = mapper.writeValueAsString(user);
return str;
}
}
此外需要在spring-mvc.xml中配置JSON的处理
<!-- 加入了JSON乱码处理及配置 -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
导入jackson依赖(Springmvc 默认用 MappingJacksonHttpMessageConverter 对 json 数据进行转换,需要加入 jackson 的包。 )
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.10.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.2</version>
</dependency>
2.6 配置解决中文乱码问题的过滤器
在 web.xml中 配置过滤器
<!--配置解决中文乱码的过滤器-->
<filter>
<filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
<!--拦截什么网址,此处设置全部拦截-->
<url-pattern>/*</url-pattern>
</filter-mapping>
2.7 常用注解
2.7.1 @RequestParam
作用: 把请求中指定名称的参数给控制器中的形参赋值。
属性:
value
:请求参数中的名称。required
:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错。
示例代码如下:
@RequestMapping("/useRequestParam")
public String useRequestParam(@RequestParam("name")String username,
@RequestParam(value="age",required=false)Integer age){
System.out.println(username+","+age);
return "success";
}
则前端参数名称必须为name、age(由于设置了 required=false
,所以该参数可不提供)
<a href="/useRequestParam?name=test">requestParam 注解</a>
2.7.2 @RequestBody
**作用:**用于获取请求体内容。直接使用得到是“key=value&key=value…”结构的数据。
(get 请求方式不适用。因为get请求会直接把参数放在地址 url 上 )
属性:
-
required
:是否必须有请求体。默认值是:true当取值为 true 时,get 请求方式会报错。如果取值 为 false,get 请求得到是 null
示例代码如下:
@RequestMapping("/useRequestBody")
public String useRequestBody(@RequestBody(required=false) String body){
System.out.println(body);
return "success";
}
post 请求 jsp代码:
用户名称:<input type="text" name="username" ><br/>
用户密码:<input type="password" name="password" ><br/>
用户年龄:<input type="text" name="age" ><br/>
<input type="submit" value=" 保存 "> </form>
------------------------------------------------------------------------------------
get 请求 jsp代码:
<a href="springmvc/useRequestBody?body=test">requestBody 注解 get 请求</a>
2.7.3 @PathVariable
**作用:**用于绑定 url 中的占位符。例如:请求 url 中 /delete/{id}
,这个 {id}
就是 url 占位符。
url 支持占位符是 Spring3.0 之后加入的。是 SpingMVC 支持 rest 风格 URL(见下一节 九、RESTful)的一个重要标志。
属性:
- value:用于指定 url 中占位符名称。
- required:是否必须提供占位符
示例代码如下:
@RequestMapping("/usePathVariable/{sid}")
public String usePathVariable(@PathVariable("sid") Integer id){
System.out.println(id);
return "success";
}
路径上的名称 @RequestMapping("/usePathVariable/{sid}")
和注解中的名称 @PathVariable("sid")
要一致,和参数名称 Integer id
无关。
前端传参
<a href="/usePathVariable/100">pathVariable 注解</a>
2.8 RESTful 风格
什么是 rest:
REST(英文:Representational State Transfer,简称 REST)
描述了一个架构样式的网络系统, 比如 web 应用程序。它首次出现在 2000 年 Roy Fielding 的博士论文中,他是 HTTP 规范的主要编写者之 一。在目前主流的三种 Web 服务交互方案中,REST 相比于 SOAP(Simple Object Access protocol,简单 对象访问协议)以及 XML-RPC 更加简单明了,无论是对 URL 的处理还是对 Payload 的编码,REST 都倾向于用更 加简单轻量的方法设计和实现。值得注意的是 REST 并没有一个明确的标准,而更像是一种设计的风格。REST式的web服务是一种ROA(面向资源的架构)- 它本身并没有什么实用性,其核心价值在于如何设计出符合 REST 风格的网络接口。
restful 的优点:
它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
restful 的特性:
-
资源(Resources)
:网络上的一个实体,或者说是网络上的一个具体信息。 它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个 URI(统一 资源定位符)指它,每种资源对应一个特定的 URI 。要 获取这个资源,访问它的 URI 就可以,因此 URI 即为每一个资源的独一无二的识别符。表现层(Representation)
:把资源具体呈现出来的形式,叫做它的表现层(Representation)。 比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
-
状态转化(State Transfer)
:每发出一个请求,就代表了客户端和服务器的一次交互过程。 HTTP 协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是 “表现层状态转化”。具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
即:
- GET(SELECT):从服务器取出资源(一项或多项)。
- POST(CREATE):在服务器新建一个资源。
- PUT(UPDATE):在服务器更新资源(客户端提供完整资源数据)。
- PATCH(UPDATE):在服务器更新资源(客户端提供需要修改的资源数据)。
- DELETE(DELETE):从服务器删除资源。
一般的 url 路径:每个方法对应不同的 url 路径
@RequeMapping("/user/delete")
public void delete(){
}
@RequeMapping("/user/findAll")
public void findAll(){
}
@RequeMapping("/user/add")
public void add(){
}
RESTful 风格的路径
@RequeMapping(value = "/user", method = RequestMethod.DELETE)
public void delete(){ // DELETE
}
@RequeMapping(value = "/user",method = RequestMethod.GET)
public void findAll(){ // GET
}
@RequeMapping(value = "/user/{id}",method = RequestMethod.GET)
public void findById(@PathVariable("id") String id){ // GET
}
@RequeMapping(value = "/user",method = RequestMethod.POST)
public void add(){ // POst
}
所有方法的路径都是一样的,通过发送的请求方式 get/post/delete/put 来确定使用哪个方法,如果两个方法的请求方式是相同的,则根据所带参数来判断调用哪个方法,比如上述代码中的 findAll 和 findById 方法
2.9 响应JSON数据
配置2.5.3中的返回JSON字符串类型内容
DispatcherServlet 会拦截到所有的资源,导致一个问题就是静态资源(img、css、js)也会被拦截到,从而不能被使用。解决该问题的就是需要配置静态资源不进行拦截,在 spring-mvc.xml
配置文件添加如下配置 :
<!-- 设置静态资源不过滤 -->
<mvc:resources location="/css/" mapping="/css/**"/> <!-- 样式 -->
<mvc:resources location="/images/" mapping="/images/**"/> <!-- 图片 -->
<mvc:resources location="/js/" mapping="/js/**"/> <!-- javascript -->
- location 元素表示 webapp 目录下的包下的所有文件
- mapping 元素表示以 /static 开头的所有请求路径,如 /static/a 或者/static/a/b
前端 jsp
<head>
<title>Title</title>
<srcipt src = "js/jquery.min.js"></srcipt>
<script type = "text/javascript">
$(function(){
$("#testJson").click(function(){
$.ajax({
type: "post",
url: "/testResponseJson",
contentType: "application/json; charset= utf-8",
data: '{"uname":"小黑", "age":20, "password":"123"}',
dataType: "json",
success:function(data){
alert(data);
// alert(data.age);
}
});
});
});
</script>
</head>
<body>
<button id = "testJson">测试 ajax 请求 json 和响应 json</button>
</body>
2.9.1 @RequestBody获取请求体数据
使用@RequestBody获取请求体数据
@Controller
public class HelloController {
@RequestMapping("/testResponseJson")
public void testResponseJson(@RequestBody String body){
System.out.println("test方法执行了"+body);
}
}
2.9.2 json 字符串 —> JavaBean 的对象
使用 @RequestBody
把 json 的字符串转换成 JavaBean 的对象
//控制器类
@Controller
public class HelloController {
@RequestMapping("/testResponseJson")
public void testResponseJson(@RequestBody User user){
System.out.println("test方法执行了"+ user);
}
}
2.9.3 JavaBean 对象 —> json 字符串
使用 @ResponseBody
把 JavaBean 对象转换成 json 字符串
//控制器类
@Controller
public class HelloController {
@RequestMapping("/testResponseJson")
public @ResponseBody User testResponseJson(@RequestBody User user){
System.out.println("test方法执行了"+ user);
user.setAge(21);
return user;
}
}
3、Mybatis
3.1 测试一个Mybatis项目
3.1.1 创建一个Maven工程并导入包
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
3.1.2 创建数据库表和对应的JavaBean
create table ssmbuild.books
(
bookID int auto_increment comment '书id' primary key,
bookName varchar(200) not null comment '书名',
bookCounts int not null comment '数量',
detail varchar(200) not null comment '描述'
);
**JavaBean:**JavaBean 是一种JAVA语言写成的可重用组件。为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器。JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性,set和get方法获取。众所周知,属性名称符合这种模式,其他Java 类可以通过自省机制(反射机制)发现和操作这些JavaBean 的属性。
注:也就是数据实体
package com.zxg.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books {
private int bookID;
private String bookName;
private int bookCounts;
private String detail;
}
该Javabean使用了lombok(用于快速生成实体类set/get/无参构造/有参构造/…的一种工具包)
@Data
:生成set/get方法
@AllArgsConstructor
:生成有参构造函数
@NoArgsConstructor
:生成无参构造函数
3.1.3 创建Mybatis全局配置文件
MyBatis 的全局配置文件包含了影响 MyBatis 行为甚深 的设置(settings)和属性(properties)信息、如数据 库连接池信息等。指导着MyBatis进行工作。我们可以 参照官方文件的配置示例
可以在项目根目录下建立 mybatis-config.xml
全局配置文件
<?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>
<!-- 在控制台中输出SQL日志 -->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<environments default="mysql">
<!-- 配置名为mysql(该名字任意)的环境 -->
<environment id="mysql">
<!-- 配置事务管理器,JDBC代表使用JDBC自带的事务提交和回滚 -->
<transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
<!-- dataSource配置数据源,此处使用MyBatis内置的数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/ssmbuild?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--<mapper class="com.zxg.dao.BookMapper"/>-->
<!--将我们写好的sql映射文件注册到全局配置文件中-->
<mappers>
<!-- 配置MyBatis需要加载的Mapper -->
<mapper resource="com/zxg/dao/BookMapper.xml"/>
</mappers>
</configuration>
3.1.4 创建一个Dao层接口以及映射Mapper的XML配置
- 创建一个dao包,该包下包含
BookMapper
接口和BookMapper.xml
package com.zxg.dao;
import com.zxg.pojo.Books;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 定义Mapper接口(DAO接口),该接口由MyBatis负责提供实现类
*/
public interface BookMapper {
// 下面这些方法的方法名必须与BookMapper.xml文件中SQL语句的id对应
// 下面这些方法的参数需要与BookMapper.xml文件中SQL语句的参数对应
//增加一本书
int addBook(Books books);
//删除一本书
int deleteBookById(@Param("bookId") int id);
//更新一本书
int updateBook(Books books);
//查询一本书
Books queryBookById(@Param("bookId") int id);
//查询全部的书
List<Books> queryAllBook();
//查询该书名的书
Books queryBookByName(@Param("queryByName") String queryByName);
}
-
创建SQL映射文件
BookMapper.xml
映射文件的作用就相当于是定义数据库如何工作,编写sql语句。这也是我们使用MyBatis时编写的最多的文件。
我们采用接口式编程,将接口中的方法和该映射文件绑定起来
- 修改名称空间 namespace 为接口的全类名
- 修改 id 为接口的方法名
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--名称空间,指定为接口的全类名-->
<mapper namespace="com.zxg.dao.BookMapper">
<!--id:唯一标识
resultType: 返回值类型
#{id}:从传递过来的参数中取出id值-->
<!-- 用insert元素定义一条insert SQL语句,id指定了这条语句的名称 -->
<insert id="addBook" parameterType="Books">
insert into ssmbuild.books (bookName, bookCounts, detail)
value (#{bookName},#{bookCounts},#{detail});
</insert>
<!-- 用delete元素定义一条delete SQL语句,id指定了这条语句的名称 -->
<delete id="deleteBookById" parameterType="int">
delete from ssmbuild.books
where bookID=#{bookId};
</delete>
<!-- 用update元素定义一条update SQL语句,id指定了这条语句的名称 -->
<update id="updateBook" parameterType="Books">
update ssmbuild.books
set bookName=#{bookName},bookCounts=#{bookCounts},detail=#{detail}
where bookID=#{bookID};
</update>
<!-- 使用Books别名指定结果集中的每条记录映射的类型(resultType) -->
<select id="queryBookById" resultType="Books">
select * from ssmbuild.books
where bookID=#{bookId};
</select>
<!-- 使用Books别名指定结果集中的每条记录映射的类型(resultType) -->
<select id="queryAllBook" resultType="Books">
select * from ssmbuild.books;
</select>
<!-- 使用Books别名指定结果集中的每条记录映射的类型(resultType) -->
<select id="queryBookByName" resultType="Books">
select * from ssmbuild.books
where bookName=#{queryByName};
</select>
</mapper>
3.1.5 测试
首先,我们加载主配置文件,生成一个 SqlSessionFactory
,再由 SqlSessionFactory 生成一个 SqlSession
,一个 SqlSession 就相当于我们的一个会话,类似于 JDBC 中的 一个连接 connection,在 SQL 语句执行完毕后,这个会话是可以被关闭的。
SqlSession 的实例不是线程安全的,因此是不能被共享的。
SqlSession 每次使用完成后需要正确关闭,这个 关闭操作是必须的。通常把这个关闭操作放到 finally 块中
@Test
public void test01() throws IOException{
// 1. 获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取sqlSession实例
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
// 3. 获取接口的实现类对象
// Mybatis会为接口自动的创建一个代理对象,由代理对象去执行增删改查方法
BookMapper mapper = sqlSession.getMapper(BookMapper.class);
// 4. 调用接口方法
BookMapper bookmapper = mapper.getEmpById(1);
System.out.println(bookmapper);
} finally {
sqlSession.close();
}
}
3.2 全局配置文件mybatis-config.xml
MyBatis 的配置文件包含了影响 MyBatis 行为甚深的 设置(settings)和属性(properties)信息。文档的顶层结构如下:
- configuration 配置
- properties 属性
- settings 设置
- typeAliases 类型命名
- typeHandlers 类型处理器
- objectFactory 对象工厂
- plugins 插件
- environments 环境
- environment 环境变量
- transactionManager 事务管理器
- dataSource 数据源
- databaseIdProvider 数据库厂商标识
- mappers 映射器
3.2.1 properties 属性
相比于上述在全局配置文件中写死的方式,我们可以利用 peoperties 将数据库配置信息提取出来
mybatis 可以使用 properties 来引入外部 properties 配置文件的内容;
resource
:引入类路径下的资源url
:引入网络路径或者磁盘路径下的资源
<properties resource="database.properties"></properties>
<!-- 随后可以使用EL表达式取值
例如:<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
-->
在 conf 文件夹下建立 dbconfig.properties 文件,将数据库配置信息写在此处
jdbc.driver=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ssmbuild?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=UTC
jdbc.user=root
jdbc.password=123456
如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:
- 在 properties 元素体内指定的属性首先被读取。
- 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根 据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
- 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性
3.2.2 environments环境
MyBatis可以配置多种环境,比如开发、测试和生产环境需要有不同的配置。
-
每种环境使用一个
environment
标签进行配置并指定唯一标识符 -
可以通过 environments 标签中的
default
属性指定 一个环境的标识符来快速的切换环境<environments default="dev_mysql"> <environment id="dev_mysql"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> <environment id="dev_oracle"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${orcl.driver}" /> <property name="url" value="${orcl.url}" /> <property name="username" value="${orcl.username}" /> <property name="password" value="${orcl.password}" /> </dataSource> </environment> </environments>
-
id
:指定当前环境的唯一标识 -
transactionManager
和dataSource
都必须有
transactionManager
type
: JDBC | MANAGED | 自定义
- JDBC:使用了 JDBC 的提交和回滚设置,依赖于从数 据源得到的连接来管理事务范围。 JdbcTransactionFactory
- MANAGED:不提交或回滚一个连接、让容器来管理 事务的整个生命周期(比如 JEE 应用服务器的上下 文)。 ManagedTransactionFactory
- 自定义:实现TransactionFactory接口,type=全类名/ 别名
dataSource
type
: UNPOOLED | POOLED | JNDI | 自定义
- UNPOOLED:不使用连接池, UnpooledDataSourceFactory
- POOLED:使用连接池, PooledDataSourceFactory
- JNDI: 在EJB 或应用服务器这类容器中查找指定的数据源
- 自定义:实现DataSourceFactory接口,定义数据源的 获取方式。
实际开发中我们使用Spring管理数据源,并进行事务控制的配置来覆盖上述配置
3.2.3 mapper映射
- mapper逐个注册SQL映射文件
<!-- mappers:将sql映射注册到全局配置中 -->
<mappers>
<!--
mapper:注册一个sql映射
注册配置文件
resource:引用类路径下的sql映射文件
com/zxg/dao/BookMapper.xml
url:引用网路路径或者磁盘路径下的sql映射文件
file:///var/mappers/BookMapper.xml
-->
<mapper resource="com/zxg/dao/BookMapper.xml"/>
</mappers>
也可以采用 注册接口 的方法
class
:引用(注册)接口,
有 sql 映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
<mapper class="com.zxg.dao.BookMapper"/>
没有sql映射文件,所有的 sql 都是利用注解写在接口上;
//查询一本书
@Select("select * from books where bookID=#{bookId}")
Books queryBookById(@Param("bookId") int id);
推荐:
- 比较重要的,复杂的 Dao 接口我们来写 sql 映射文件
- 不重要,简单的 Dao 接口为了开发快速可以使用注解;
批量注册
这种方式要求SQL映射文件名必须和接口名相同并且在同一目录下
<mappers>
<!-- 批量注册: -->
<package name="com.zxg.dao"/>
<mappers>
3.3 SQL映射文件
映射文件指导着MyBatis如何进行数据库增删改查, 有着非常重要的意义;
-
cache –命名空间的二级缓存配置
-
cache-ref – 其他命名空间缓存配置的引用
-
resultMap – 自定义结果集映射
-
parameterMap – 已废弃!老式风格的参数映射
-
sql –抽取可重用语句块。
-
insert – 映射插入语句
-
update – 映射更新语句
-
delete – 映射删除语句
-
select – 映射查询语句
3.3.1 insert获取自增主键的值
若数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server), 则可以设置 useGeneratedKeys=”true”
,然后再把 keyProperty
设置到目标属性上。
<insert id="addBook" parameterType="Books"
useGeneratedKeys="true" keyProperty="id">
insert into ssmbuild.books (bookName, bookCounts, detail)
value (#{bookName},#{bookCounts},#{detail});
</insert>
3.3.2 参数处理
3.3.2.1 单个参数
单个参数:mybatis不会做特殊处理,#{参数名/任意名}:取出参数值
例如:
//删除一本书
int deleteBookById(@Param("bookId") int id);
不一定非要通过 #{id}
取出参数值,任意参数名都可取出,比如 #{abc}
<delete id="deleteBookById" parameterType="int">
delete from ssmbuild.books
where bookID=#{bookId};
</delete>
3.3.2.2 多个参数
多个参数的情况下,按照上面的方法取值会报错,比如:
public Employee getEmpByIdAndLastName(Integer id,String lastName);
-------------------------------------------------------------------
<select id="getEmpByIdAndLastName" resultType="Employee">
select * from tbl_employee where id = #{id} and last_name=#{lastName}
</select>
报错如下:
org.apache.ibatis.binding.BindingException:
Parameter 'id' not found.
Available parameters are [1, 0, param1, param2]CopyErrorOK!
任意多个参数,都会被 MyBatis 重新包装成一个Map传入。
key:param1…paramN, 或者参数的索引也可以
value:传入的参数值
#{ }
就是从 map 中获取指定的 key 的值
<select id="getEmpByIdAndLastName" resultType="com.smallbeef.mybatis.bean.Employee">
select * from tbl_employee where id = #{param1} and last_name= #{param2}
</select>
3.3.2.3 @Param 命名参数
多个参数用上述这样的方法看起来不太直观,于是我们可以使用注解 @Param
为参数起一个名字,MyBatis就会将这些参数封装进 map 中,key 就是我们自己指定的名字
举例如下:
public Employee getEmpByIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName);
---------------------------------------------------------------------------------------
<select id="getEmpByIdAndLastName" resultType="com.smallbeef.mybatis.bean.Employee">
select * from tbl_employee where id = #{id} and last_name = #{lastName}
</select>
3.3.2.4 POJO
如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;
#{属性名}
:取出传入的pojo的属性值
举例如下:
public boolean updateEmp(Employee employee);
-------------------------------------------------------------
<update id="updateEmp">
update tbl_employee
set last_name=#{lastName},email=#{email},gender=#{gender}
where id=#{id}
</update>
3.3.2.5 Map
如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以封装多个参数为 map,直接传递
#{key}
:取出map中对应的值
举例如下:
public Employee getEmpByMap(Map<String, Object> map);
---------------------------------------------------------------------
<select id="getEmpByMap" resultType="com.smallbeef.mybatis.bean.Employee">
select * from tbl_employee where id=${id} and last_name=#{lastName}
</select>
---------------------------------------------------------------------
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//Employee employee = mapper.getEmpByIdAndLastName(1, "tom");
Map<String, Object> map = new HashMap<>();
map.put("id", 2);
map.put("lastName", "Tom");
Employee employee = mapper.getEmpByMap(map);
3.3.2.6 参数处理 $ 和 # 的区别
#{}
:不可以获取 map 中的值或者 pojo 对象属性的值;${}
:可以获取 map 中的值或者 pojo 对象属性的值;
select * from tbl_employee where id = ${id} and last_name= #{lastName}
输出如下:
Preparing: select * from tbl_employee where id=2 and last_name=?
区别:
#{}
: 是以预编译的形式,将参数设置到 sql 语句中,防止 sql 注入${}
: 取出的值直接拼装在sql语句中;会有安全问题;
大多情况下,我们去参数的值都应该去使用 #{}
;
原生jdbc不支持占位符的地方我们就可以使用 ${}
进行取值 比如分表、排序。。。;
举例如下:
按照年份分表拆分
select * from ${year}_salary where xxx;
select * from tbl_employee order by ${f_name} ${order}