JSP入门

JSP入门&MVCL&案例

1 JSP

1.1 JSP概述

  • 什么是JSP?

    JSP:Java Server Pages (Java服务器端页面),其实就在HTML中嵌入Java代码。

    img

  • 为什么学习JSP

    SUN公司提供了动态网页开发技术:Servlet。Servlet自身有一些缺点,SUN公司发现了这些问题,推出了一个新的动态网页开发技术JSP。
    Servlet的缺点:
     Servlet需要进行配置,不方便维护
     Servlet很难向网页中输出HTML页面内容

1.2 JSP的运行原理

  • JSP文件翻译成Java文件,将这个Java文件编译生成class文件,运行class文件。

    img

1.3 JSP的脚本元素


  • JSP = HTML + Java代码 + JSP自身东西

    JSP的脚本元素就是在JSP中嵌入Java代码。

    • 声明标签

       <%! 变量或方法声明 %>
       写在这个脚本中的代码,翻译成Servlet内部的成员变量或成员方法。

    • 表达式标签

       <%= 表达式 %>
       写在这个脚本中的代码,翻译成方法内部的out.print();当中的内容。

    • 程序代码标签

       <% 程序代码 %>
       写在这个脚本中的代码,翻译成方法内部的局部变量或方法内部代码片段。

1.4 JSP开发模式


  • 动态网页开发模式的发展

    img

1.5 路径的分类

  • 绝对路径(通常)

     绝对路径的写法:
     通常以 / 开始的路径
    使用绝对路径,不需要关心当前文件和要请求的文件的相对位置的关系!!!
     注意:
     绝对路径分成服务器端路径和客户端路径
     客户端路径 需要带工程名
     服务器端路径 不需要带工程名

    img

2.请求转发和重定向

2.1 请求转发

  • 请求转发的的写法

    通过ServletRequest对象获得RequestDispatcher对象。

    img

    再根据RequestDispatcher中的方法进行请求转发。

    img

  • 请求转发的代码实现

    img

  • 请求转发的效果

    img

2.2 重定向

  • 重定向的写法

    通过HttpServletResponse对象中的以下方法实现重定向

    img

  • 重定向的代码实现

    img

  • 重定向的效果

    img

2.3 请求转发和重定向的区别

  • 请求转发和重定向原理

    img

  • 请求转发和重定向区别总结

     请求转发是一次请求一次响应,而重定向是两次请求两次响应。
     请求转发地址栏不会变化的,重定向地址栏发生变化。
     请求转发路径不带工程名,重定向需要带工程名路径。
     请求转发只能在本网站内部,重定向可以定向到任何网站。

3.登陆案例分析过程

1559650742626

linux命令

linux命令

1. 切换目录命令cd

cd home 切换到home目录

1560598559248

cd .. 切换到上一层目录

1560598713751

cd / 切换到系统根目录

1560598730100

cd ~ 切换到用户主目录

1560598777737

cd - 切换到上一个所在目录

1560598761692

2. 列出文件列表ls ll

ls -a 显示所有文件包括隐藏文件(文件名以.开头)

1560598995895

ls -l (简写为ll) 使用较长格式列出信息

1560599098101

dir 显示文件(与ls功能相同,很少用)

1560599191717

3.创建与删除目录 mkdir rmdir

mkdir aaa 在当前目录下创建aaa目录

1560599830981

mkdir –p bbb/ccc 级联创建bbb以及ccc目录

1560600185000

rmdir aaa 在当前目录下删除aaa目录

1560600323068

rm -r 目录名 递归删除目录

1560600776964

rm -f 目录名 不询问直接删除目录

rm -rf 目录名 不询问递归删除目录

1560600833612

4.创建文件

touch a.txt 在当前目录下创建a.txt文件

1560600502429

cp a.txt ccc 将a.txt文件复制到ccc目录中

1560600655837

cp a.txt b.txt 将a.txt复制到当前目录并命名为b.txt

1560600720534

mv a.txt c.txt 在当前目录下将a.txt重命名为c.txt

mv c.txt ccc 将c.txt文件移动到ccc目录下

5.编辑文件

有三种模式 命令行 插入 底行
通过 vi(vim) 文件名 就可以对文件进行操作
当操作时,开始是命令行模式 按I o a 切换到插入模式
按esc 可以在重新切换到命令行模式
在命令行模式下按 “:” 就可以切换到底行模式。

在命令行模式下可以使用一些快捷键
在Linux下一般使用vi编辑器来编辑文件。 vi既可以查看文件也可以编辑文件。 三种模式:命令行、插入、底行模式。
切换到命令行模式:按Esc键;
切换到插入模式:按 i 、o、a键;
i 在当前位置前插入
I 在当前行首插入
a 在当前位置后插入
A 在当前行尾插入
o 在当前行之后插入一行
O 在当前行之前插入一行

切换到底行模式:按 :(冒号);

打开文件:vim file
退出:esc :q
修改文件:输入i进入插入模式
保存并退出:esc:wq

不保存退出:esc:q!

快捷键:命令行模式下
dd – 快速删除一行
yy - 复制当前行
nyy - 从当前行向后复制几行
p - 粘贴
R – 替换

1560601084964

6.浏览文件

cat操作

1560601702501

more和less操作类似

Listner&Filter

Listener&Filter

1.Listener

1.1 监听器概述

什么是监听器

​ 监听器就是一个实现了特定接口的Java类,这个Java类用于监听另一个Java类的方法调用或者属性的改变。当被监听对象发生上述事件后,监听器某个方法将会立即被执行。

监听器的术语

 事件源:指的是被监听对象(汽车)
 监听器:指的是监听的对象(报警器)
 事件源和监听器绑定:在汽车上安装报警器
 事件:指的是事件源对象的改变(踹了汽车一脚)—-主要功能获得事件源对象。

为事件源绑定监听器,当事件源发生事件,那么监听器就会去处理(执行对应的方法)

  1. 事件源
  2. 监听器(方法)
  3. 为事件源绑定监听器

1.2 监听器的执行流程

img

  1. 明确事件源和监听器
  2. 明确如何为事件源绑定监听器
  3. 明确事件的触发
  4. 明确事件触发之后会执行监听器中的哪个方法

1.3 Servlet中的监听器

  • Servlet中的监听器简介

    在Servlet中定义了多种类型的监听器,它们用于监听的事件源分别是ServletContext、HttpSession和ServletRequest这三个域对象。

  • Servlet中的监听器的分类

     一类:监听三个域对象的创建和销毁的监听器(三个)
     二类:监听三个域对象的属性变更(属性添加、移除、替换)的监听器(三个)
     三类:监听HttpSession中JavaBean的状态改变(钝化、活化、绑定、解除绑定)的监听(两个)

  • 第一类

    3个监听域对象的创建和销毁的监听器

    • ServletContextListener

      用来监听ServletContext域对象的创建和销毁的监听器。

      ServletContext
       创建:在服务器启动的时候,为每个web应用创建单独的ServletContext对象。
       销毁:在服务器关闭的时候,或者项目从web服务器中移除的时候。

    • HttpSessionListener

      用来监听HttpSession对象的创建和销毁。

      HttpSession

       创建:
       服务器端第一次调用getSession()方法时候。
       销毁:
       非正常关闭服务器(正常关闭服务器session会被序列化)。
       Session过期(默认过期时间30分钟)。
       手动调用session.invalidate()方法。

      1
      2
      3
      	访问HTML是否创建Session		:不会
       访问JSP是否创建Session :会
       访问Servlet是否创建Session :不会(默认没有调用getSession方法)
    • ServletRequestListener

      用来监听ServletRequest对象的创建和销毁

      ServletRequest

       创建
       从客户端向服务器发送一次请求,服务器就会创建request对象。
       销毁
       服务器对这次请求作出了响应之后,request对象就销毁了。

      1
      2
      3
      	访问HTML页面是否创建请求对象	:会
       访问JSP页面是否创建请求对象 :会
       访问Servlet是否创建请求对象 :会
  • 第二类

    3个用来监听域对象属性变化的监听器

    • ServletContextAttributeListener

      监听ServletContext对象中的属性变更(属性添加,移除,替换)的监听器

      img

    • HttpSessionAttributeListener

      监听HttpSession对象中的属性变更(属性添加,移除,替换)的监听器

      img

    • ServletRequestAttributeListener

      监听ServletRequest对象中的属性变更(属性添加,移除,替换)的监听器

      img

  • 第三类

    保存在Session域中的Java类可以有多种状态:绑定到session中;从session中解除绑定;随session对象持久化到一个存储设备中(钝化);随session对象从一个存储设备中恢复(活化)。
    Servlet对方中定义了两个特殊的监听的接口来帮助Java类了解自己在Session域中的状态:
    HttpSessionBindingListener接口 监听绑定和解除绑定
    HttpSessionActivationListener接口, 监听钝化和活化
    实现这两个接口的类不需要在web.xml中进行配置。

    • HttpSessionBindingListener

      监听Java类在HttpSession中的绑定和解除绑定的状态的监听器:

      img

    • HttpSessionActivationListener

      监听HttpSession中Java类的钝化和活化监听器。

      img

    • 配置完成session的序列化和反序列化

      Context标签可以配置在:

      tomcat/conf/context.xml :所有tomcat下虚拟主机和虚拟目录下的工程都会序列化session

      tomcat/conf/Catalina/localhost/context.xml :localhost虚拟主机下的所有项目会序列化session

      工程/META-INF/context.xml :当前工程才会序列化session。

      1
      2
      3
      4
      5
      <Context>
      <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
      <Store className="org.apache.catalina.session.FileStore" directory="itheima"/>
      </Manager>
      </Context>

2.Filter

Filter称为过滤器,它是Servlet技术中最实用的技术,web开发人员通过Filter技术,对web服务器所管理的资源(JSP,Servlet,静态图片或静态html文件)进行拦截,从而实现一些特殊的功能。

Filter

就是过滤从客户端向服务器发送的请求。

2.1 Filter过滤原理

img

2.2 FilterChain

FilterChain过滤器链:在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称为是一个过滤器链。

Web服务器根据Filter在web.xml文件中的注册顺序(mapping的配置顺序)决定先调用那个Filter。依次调用后面的过滤器,如果没有下一个过滤器,调用目标资源

img

2.3 Filter的生命周期

Filter的创建和销毁是由web服务器负责。Web应用程序启动的时候,web服务器创建Filter的实例对象。并调用其init方法进行初始化(filter对象只会创建一次,init方法也只会执行一次)。

每次filter进行拦截的时候,都会执行doFilter的方法。

当服务器关闭的时候,应用从服务器中移除的时候,服务器会销毁Filter对象。

2.4 FilterConfig对象

用来获得Filter的相关的配置的对象。

img

2.5 Filter相关配置

  • 的配置

    完全路径匹配 :以/开始 比如/aaa /aaa/bbb

    目录匹配 :以/开始 以结束 比如/ /aaa/* /aaa/bbb/*

    扩展名匹配 :不能以/开始 以开始 比如.jsp *.do *.action

  • 的配置

    专门以Servlet的配置的名称拦截Servlet。

  • 的配置

     默认的情况下过滤器会拦截请求。如果进行转发(需要拦截这次转发)。
     dispatcher的取值
     REQUEST :默认值。默认过滤器拦截的就是请求。
     FORWARD:转发。
     INCLUDE :页面包含的时候进行拦截
     ERROR :页面出现全局错误页面跳转的时候进行拦截

3.案例实现

3.1 统计当前在线人数

分析: 1.在启动服务器时,在servletContext域中初始化在线人数为0.

​ 2.用户登录,就在session域中存入用户信息,获取ServletContext域中的在线人数,加一后,再存入;

​ 3.session过期后,获取ServletContext域中的在线人数,减一后,再存入;

servletContextListener

1
2
3
4
5
6
7
8
9
10
11
12
13
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class myServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
//初始化count=0
sce.getServletContext().setAttribute("count", 0);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}

HttpSessionListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class myHttpSessionListener implements HttpSessionListener {

@Override
public void sessionCreated(HttpSessionEvent se) {
HttpSession session = se.getSession();
System.out.println(session.getId() + "上线了");
//获取servletContext中的在线人数
Integer count = (Integer) session.getServletContext().getAttribute(
"count");
count++;
//count+1后存回 session.getServletContext().setAttribute("count", count);
}

@Override
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
System.out.println(session.getId() + "离线了");
Integer count = (Integer) session.getServletContext().getAttribute(
"count");
count++; session.getServletContext().setAttribute("count", count);
}
}

页面

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>当前在线人数为:${count }</h1>
</body>
</html>

3.2 权限验证及通用字符集编码过滤器

Controller

loginServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import java.io.IOException;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.itheima.domian.User;
import com.itheima.model.loginModel;

/**
* Servlet implementation class loginServlet
*/
public class loginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
try {
// 获取数据
String username = request.getParameter("username");
String password = request.getParameter("password");
//System.out.println(username + "---" + password);
// 封装数据
User user = new User();
user.setUsername(username);
user.setPassword(password);
// System.out.println(user.getUsername() + "--" + user.getPassword());
// 处理数据
loginModel userModel = new loginModel();
User existuser = userModel.login(user);
// 判断数据
if (existuser == null) {
// 登陆失败
request.setAttribute("msg", "用户名或密码错误!"); request.getRequestDispatcher("/login.jsp").forward(request,response);
} else {
// 登陆成功
//在Session中存入用户信息 request.getSession().setAttribute("user", existuser);
//跳转到成功页面 response.sendRedirect("/web05_login/jsp/success.jsp");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

myServletRequest(采用装饰者模式加强方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.io.UnsupportedEncodingException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
//增强request的getparameter方法
//修饰者增强方法需满足
//1.增强类和被增强类实现相同的接口
//2.增强类中有被增强类的引用
//继承HttpServletRequestWrapper HttpServletRequestWrapper实现了httpServletRequest接口
public class myServletRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
// 引用了HttpServletRequest对象request
public myServletRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
if (request.getMethod().equals("GET")) {
String value = super.getParameter("name");
try {
value = new String(value.getBytes("ISO-8859-1"), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return value;
} else if (request.getMethod().equals("POST")) {
try {
request.setCharacterEncoding("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return super.getParameter(name);
}
}

Domain

user

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class User {
private int uid;
private String username;
private String password;
private String nickname;
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
}

Filter

GennericEncoding(通用字符集编码过滤)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import com.itheima.controller.myServletRequest;

/**
* Servlet Filter implementation class GenericEncoding
*/
public class GenericEncoding implements Filter {

public GenericEncoding() {
}

public void destroy() {
}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
//调用增强的方法
myServletRequest myReq = new myServletRequest(req);
chain.doFilter(myReq, response);
}

public void init(FilterConfig fConfig) throws ServletException {
}
}

loginFilet(权限过滤)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class loginFilter implements Filter {

@Override
public void init(FilterConfig filterConfig) throws ServletException {
}

@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest res = (HttpServletRequest) request;

HttpSession session = res.getSession();
if (session.getAttribute("user") == null) {
// 未登录
res.setAttribute("msg", "你还未登陆,没有权限访问该页面"); res.getRequestDispatcher("/login.jsp").forward(request, response);
} else {
chain.doFilter(request, response);
}
}

@Override
public void destroy() {
}
}

Model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import com.itheima.domian.User;
import com.itheima.utils.JDBCUtils;

public class loginModel {
public User login(User user) throws SQLException {
QueryRunner q = new QueryRunner(JDBCUtils.getDataSource());
User existuser = q.query(
"select * from user where username = ? and password = ?",
new BeanHandler<User>(User.class), user.getUsername(),
user.getPassword());
return existuser;
}
}

Utils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.sql.Connection;
import java.sql.SQLException;

import org.apache.tomcat.jdbc.pool.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JDBCUtils {
private static final ComboPooledDataSource d = new ComboPooledDataSource();

public static Connection getConnection() throws SQLException {
return d.getConnection();
}

public static ComboPooledDataSource getDataSource() {
return d;
}
}

页面

login.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登陆</title>
</head>
<body>
<h1>登陆页面</h1>
<h3>
<font color="red">${msg }</font>
</h3>
<form action="/web05_login/loginServlet" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="登陆"></td>
</tr>
</table>
</form>
</body>
</html>

success.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>
欢迎!<font>${user.nickname }</font>
</h1><a href="/web05_login/jsp/sub.jsp">提交数据</a>
</body>
</html>

配置文件

c3p0-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///web02</property>
<property name="user">root</property>
<property name="password">abc</property>

<property name="initialPoolSize">5</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
</c3p0-config>

Maven工程分层

Maven工程分层

工程分层后的开发:所有的service和dao的代码都在一起,增强程序的通用性,降低了代码的耦合性

1562675358781

聚合(多模块)和继承

1562675380479

特点1(继承):

parent父工程:存放项目的所有jar包。

ssm_web和ssm_service和ssm_dao有选择的继承jar包,并在自己的工程中使用。

特点2(聚合):

ssm_web依赖于ssm_service,ssm_service依赖于ssm_dao,我们启动ssm_web,便可以访问我们的程序。

执行安装的时候,执行ssm_parent,就可以将所有的子工程全部进行安装。

理解继承和聚合总结:

通常继承和聚合同时使用。

  • 何为继承?

继承是为了消除重复,如果将 dao、 service、 web 分开创建独立的工程则每个工程的 pom.xml 文件中的内容存在重复,比如:设置编译版本、锁定 spring 的版本的等,可以将这些重复的 配置提取出来在父工程的 pom.xml 中定义。

  • 何为聚合?

项目开发通常是分组分模块开发, 每个模块开发完成要运行整个工程需要将每个模块聚合在 一起运行,比如: dao、 service、 web 三个工程最终会打一个独立的 war 运行。

1562675405045

1.工程分层开发案例

1.1 ssm-parent

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.cyannote</groupId>
<artifactId>ssm_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>ssm_model</module>
<module>ssm_dao</module>
<module>ssm_service</module>
<module>ssm_web</module>
</modules>
<packaging>pom</packaging>

<!--特殊属性定义,一般是版本号-->
<properties>
<spring.version>5.0.2.RELEASE</spring.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<mysql.version>5.1.6</mysql.version>
<mybatis.version>3.4.5</mybatis.version>
<aspectjweaver.version>1.6.8</aspectjweaver.version>
<junit.version>4.12</junit.version>
<jsp-api.version>2.0</jsp-api.version>
<servlet-api.version>2.5</servlet-api.version>
<jstl.version>1.2</jstl.version>
<mybatis-spring.version>1.3.0</mybatis-spring.version>
<druid.version>1.0.9</druid.version>
<!--文件的编码格式-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>

<!--
jar包管理
dependencyManagement:并非导入依赖,而只是管理依赖(可供子工程选择)
-->
<dependencyManagement>
<!--引入依赖-->
<dependencies>
<!-- spring -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectjweaver.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>

<!--spring包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>

<!--用于SpringMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>

<!--用于数据库源相关操作-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>

<!--ServletAPI-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${servlet-api.version}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>${jsp-api.version}</version>
<scope>provided</scope>
</dependency>

<!--jstl标签-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>

<!--MySQL数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>

<!--测试框架-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>


<!-- log start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->

<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>

<!--MyBatis集成Spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>

<!--数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
</dependencies>
</dependencyManagement>


<build>
<pluginManagement>
<!--插件-->
<plugins>
<!--tomcat插件-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<!--插件使用的相关配置-->
<configuration>
<!--端口号-->
<port>18081</port>
<!--写当前项目的名字(虚拟路径),如果写/,那么每次访问项目就不需要加项目名字了-->
<path>/</path>
<!--解决get请求乱码-->
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>

</project>

1.2 ssm-domain

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ssm_parent</artifactId>
<groupId>com.cyannote</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ssm_model</artifactId>
<packaging>jar</packaging>
</project>

创建实体类Items.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class Items implements Serializable {
private Integer id;
private String name;
private Float price;
private String pic;
private Date createtime;
private String detail;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Float getPrice() {
return price;
}

public void setPrice(Float price) {
this.price = price;
}

public String getPic() {
return pic;
}

public void setPic(String pic) {
this.pic = pic;
}

public Date getCreatetime() {
return createtime;
}

public void setCreatetime(Date createtime) {
this.createtime = createtime;
}

public String getDetail() {
return detail;
}

public void setDetail(String detail) {
this.detail = detail;
}

@Override
public String toString() {
return "Items{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
", pic='" + pic + '\'' +
", createtime=" + createtime +
", detail='" + detail + '\'' +
'}';
}
}

1.3 ssm-dao

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ssm_parent</artifactId>
<groupId>com.cyannote</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>ssm_dao</artifactId>
<packaging>jar</packaging>

<dependencies>
<!--model依赖-->
<dependency>
<groupId>com.cyannote</groupId>
<artifactId>ssm_model</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<!--mybatis集成spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
<!--数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--springJDBC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<!--log start-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<!--log end-->
</dependencies>

</project>

applicationContext-dao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///test03"></property>
<property name="username" value="root"></property>
<property name="password" value="abc"></property>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启dao包扫描-->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.cyannote.ssm.dao"></property>
</bean>
</beans>

创建持久层接口ItemsDao.java

1
2
3
4
5
6
7
8
public interface ItemsDao {
//查询所有
@Select(value = "select * from items")
List<Items> findAll();
//保存
@Insert(value = "insert into items (id,name,price,pic,createtime,detail) values ( #{id}, #{name}, #{price}, #{pic}, #{createtime}, #{detail})")
int saveItems(Items items);
}

1.4 ssm-service

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ssm_parent</artifactId>
<groupId>com.cyannote</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>ssm_service</artifactId>
<packaging>jar</packaging>

<!---->
<dependencies>
<!--dao依赖-->
<dependency>
<groupId>com.cyannote</groupId>
<artifactId>ssm_dao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!--切面编程-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<!--spring事务-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
</dependencies>
</project>

applicationContext-service.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd ">
<!--导入applicationContext-dao.xml-->
<import resource="applicationContext-dao.xml"></import>
<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启事务注解扫描-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>

创建业务层接口ItemsService及其实现类ItemsServiceImpl

ItemsService.java

1
2
3
4
5
6
public interface ItemsService {
//查询所有
List<Items> findAll();
//保存
int saveItems(Items items);
}

ItemsServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service("itemsService")
@Transactional
public class ItemsServiceImpl implements ItemsService {
@Autowired
private ItemsDao itemsDao;
@Override
public List<Items> findAll() {
return itemsDao.findAll();
}

@Override
public int saveItems(Items items) {
return itemsDao.saveItems(items);
}
}

1.5 ssm-web

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ssm_parent</artifactId>
<groupId>com.cyannote</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>ssm_web</artifactId>
<packaging>war</packaging>

<dependencies>
<!--service依赖-->
<dependency>
<groupId>com.cyannote</groupId>
<artifactId>ssm_service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--springmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<!--sevletApi-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<!--编译,测试时有效,运行时无效-->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<scope>provided</scope>
</dependency>

<!--标签库-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
</dependency>
</dependencies>

<build>
<!--插件-->
<plugins>
<!--tomcat插件-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<!--插件使用的相关配置-->
<configuration>
<!--端口号-->
<port>18081</port>
<!--写当前项目的名字(虚拟路径),如果写/,那么每次访问项目就不需要加项目名字了-->
<path>/</path>
<!--解决get请求乱码-->
<uriEncoding>UTF-8</uriEncoding>
</configuration>

</plugin>

</plugins>
</build>
</project>

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!--核心控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<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>
<!--post请求中文乱码过滤-->
<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>
</web-app>

springmvc.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--导入applicationContext-service.xml-->
<import resource="applicationContext-service.xml"></import>
<!--开启扫描-->
<context:component-scan base-package="com.cyannote.ssm"></context:component-scan>
<!--注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--图形解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--静态资源-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
</beans>

创建ItemsController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Controller
@RequestMapping(path = "/items")
public class ItemsController {
@Autowired
private ItemsService itemsService;

@RequestMapping(path = "/findAll")
public String findAll(Model model){
List<Items> list = itemsService.findAll();
model.addAttribute("items",list);
return "success";
}

@RequestMapping(path = "/save")
public String saveItems(Items items){
int rows = itemsService.saveItems(items);
if (rows > 0){
return "redirect:/items/findAll";
}else {
return "redirect:index.jsp";
}
}
}

编写页面

index.jsp

1
2
3
4
5
6
7
8
9
10
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>查询所有</h3>
<<a href="/items/findAll">查询所有</a>
</body>
</html>

succes.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>访问成功!</h3>
<hr>
<h3>新增/查询</h3>
<form method="post" action="/items/save">
<table>
<tr>
<td>名称</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>价格</td>
<td><input type="text" name="price"></td>
</tr>
<tr>
<td>图片</td>
<td><input type="text" name="pic"></td>
</tr>
<tr>
<td>创建日期</td>
<td><input type="text" name="createtime"></td>
</tr>
<tr>
<td>详情</td>
<td><input type="text" name="detail"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" name="保存"></td>
</tr>
</table>
</form>
<hr>
<table border="1">
<tr>
<td>ID</td>
<td>name</td>
<td>price</td>
<td>pic</td>
<td>createTime</td>
<td>detail</td>
</tr>

<c:forEach items="${items}" var="item">
<tr>
<td>${item.id}</td>
<td>${item.name}</td>
<td>${item.price}</td>
<td>${item.pic}</td>
<td>${item.createtime}</td>
<td>${item.detail}</td>
</tr>
</c:forEach>

</table>
</body>
</html>

MyBatis学习笔记

mybatis

1.mybatis概述

1. 1 什么是mybatis

1:Hibernate就是一个持久层的的框架。对JDBC做了轻量级封装
2:Mybatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射

1.2 mybatis概述

1:它是基于Java编写的持久层框架,使开发者不必关心传统jdbc的api,只关心sql语句本身

2:mybatis 通过 xml或注解的方式将要执行的各种 statement 配置起来

3:采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作

2.mybatis的CRUD操作

2.1 mybatis查询所有的步骤

1.创建工程

2.导入maven坐标

1
2
3
4
5
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>

3.创建实体类

1
2
3
4
5
6
7
8
public class User implements Serializable {

private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
}

4.导入log4j.properties文件

查询所有

5.创建SqlMapConfig.xml(省略)

1
2
3
4
5
6
7
8
<typeAliases>用于指定别名
<environments>配置环境
<environment>
<transactionManager type="JDBC">配置事务管理策略
<dataSource type="POOLED">配置数据区连接方式
<property name="" value=""/>配置数据库连接参数
<mappers>
<package name="com.cyannote.dao"/>用于指定dao接口所在的包

6.创建UserDao.java

1
2
3
4
5
6
7
public interface UserDao {
/**
* 查询所有用户
* @return
*/
List<User> findAll();
}

7.创建UserDao.xml

1
2
3
4
5
6
 <mapper namespace="com.cyannote.dao.UserDao">
<!--查询所有-->
<select id="findAll" resultType="com.cyannotea.domain.User">
select * from user
</select>
</mapper>

8.创建测试类MybatisTest .java(省略)

2.2 #{}与${}的区别

#{}表示一个占位符号
通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
${}表示拼接sql串
通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。

2.3 SqlMapConfig.xml配置文件

SqlMapConfig.xml中配置的内容和顺序如下:
properties(属性)

1
2
SqlMapConfig.xml配置文件
<properties resource="jdbcConfig.properties"></properties>

settings(全局配置参数)
typeAliases(类型别名)

1
2
3
<!-- 用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
<!--<package name="com.cyannote.domain"></package>-->
</typeAliases>

typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)

1
2
3
4
5
<package name=""/> 
注册指定包下的所有mapper接口
如:<package name="com.caynnote.dao"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
应用场景:即可以使用XML配置文件,也可以使用注解。

2.4 Mybatis映射文件的SQL深入

动态SQL的标签

1
2
3
4
5
6
7
8
9
10
<!--根据条件查询-->
<select id="findByCondition" parameterType="user" resultMap="userMap">
select * from user where 1=1
<if test="userName != null">
and username = #{userName}
</if>
<if test="userSex != null">
and sex = #{userSex}
</if>
</select>

动态SQL之标签

1
2
3
4
5
6
7
8
9
10
11
12
<!--where标签的使用-->
<select id="findByCondition" parameterType="user" resultMap="userMap">
select * from user
<where>
<if test="userName != null">
and username = #{userName}
</if>
<if test="userSex != null">
and sex = #{userSex}
</if>
</where>
</select>

动态标签之标签

1
2
3
4
5
6
7
8
9
10
11
<!--foreach标签的使用-->
<select id="findInIds" parameterType="queryVo" resultMap="userMap">
select * from user
<where>
<if test="ids != null and ids.size()>0">
<foreach collection="ids" open=" and id in (" close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>

3.mybatis的多表查询

3.1 多对一(一对一):

方案一:创建新的javabean(包含两张表的字段)

第一步:sql语句
SELECT a.*,u.username,u.address FROM account a,USER u WHERE u.id = a.uid
第二步:在com.cyanote.domain中创建AccountUser.java
创建一个新的javabean

1
2
3
4
public class AccountUser extends Account implements Serializable {
private String username;
private String address;
}

第三步:AccountDao.java
/**

​ *查询账号和客户的信息
​ */
​ List findByAccountUser();
第四步:AccountDao.xml

1
2
3
4
<!-- 查询所有 -->
<select id="findByAccountUser" resultType="com.cyannote.domain.AccountUser">
SELECT a.*,u.username,u.address FROM account a,user u WHERE u.id = a.uid
</select>

第五步:MybatisTest.java

方案二:直接用用Account对象封装

第一步:sql语句
select u.*,a.id as aid,a.uid,a.money from account a,user u where u.id = a.uid
第二步:修改Account.java
在Account中封装User

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Account implements Serializable{
private Integer id;
private Integer uid;
private Double money;
private User user;

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}
}

第三步:AccountDao.java

1
2
3
4
5
/**

*查询账号和客户的信息(方案二:直接用Account对象封装)
*/
List<Account> findByAccountUser2();

第四步:AccountDao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--定义resultMap对象,用来封装账号信息-->
<resultMap id="accountMap" type="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!--association用来关联对象(用于多对一,或一对一),property代表加载对象,javaType代表加载对象的数据类型,可以写成com.cyannote.domain.User-->
<association property="user" javaType="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
</association>
</resultMap>
<!-- 查询所有(方案二:直接用Account对象封装) -->
<select id="findByAccountUser2" resultMap="accountMap">
select u.*,a.id as aid,a.uid,a.money from account a,user u where u.id = a.uid
</select>

第五步:MybatisTest.java

4.mybatis的延迟加载策略

4.1 什么是延迟加载

什么是延迟加载
在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)
什么是立即加载
不管用不用,只要一调用方法,马上发起查询。
应用场景:
在对应的四种表关系中:一对多,多对一,一对一,多对多
一对多,多对多:通常情况下我们都是采用延迟加载。(建议)
多对一,一对一:通常情况下我们都是采用立即加载。(建议)

4.2 配置延迟加载

配置SqlMapConfig.xml:**(全局配置)**

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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>
<properties resource="jdbcConfig.properties"></properties>
<!--需要配置延迟加载策略-->
<settings>
<!--打开延迟加载的开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--将积极加载改为消息加载,即按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

配置AccountDao.xml:(局部配置)

AccountDao.xml中使用fethcType属性用来控制延迟检索和立即检索:

1
2
3
4
5
6
<!--fetchType:
eager:立即检索
lazy:延迟检索
-->
<association property="user" javaType="user" select="com.cyannote.dao.UserDao.findById" column="uid" fetchType="eager">
</association>

只配置一处就可实现延迟加载,若两处都配置,局部配置优先级别更高.

5.mybatis缓存

Mybatis中缓存分为一级缓存,二级缓存
一级缓存:是SqlSession级别的缓存(线程级别的缓存)
二级缓存:是SqlSessionFactory级别的缓存(进程级别的缓存)
一个SqlSessionFactory存在多个SqlSession。

5.1 一级缓存(SqlSession)

​ 它指的是Mybatis中SqlSession对象的缓存。默认是开启SQLSession缓存的.

​ 当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。一级缓存中存放的是一个对象.

​ 该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用,如果没有再查询数据库。

​ 一级缓存是SqlSession 范围的缓存,当调用SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。

​ 当SqlSession对象消失时,mybatis的一级缓存也就消失了。

img

​ 第一次发起查询用户id 为1 的用户信息,先去找缓存中是否有id 为1 的用户信息,如果没有,从数据库查询用户信息。 得到用户信息,将用户信息存储到一级缓存中。 如果sqlSession 去执行commit 操作(执行插入、更新、删除),清空SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

​ 第二次发起查询用户id 1 id 1

5.2 二级缓存(SqlSessionFactory)

​ 它指的是Mybatis中SqlSessionFactory对象的级别缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

​ 二级缓存的使用步骤:

​ 第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)

​ 因为cacheEnabled 的取值默认就为true,所以这一步可以省略不配置。为true 代表开启二级缓存; 为false 代表不开启二级缓存。

1
2
3
4
5
<!--需要配置延迟加载策略-->
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>

​ 第二步:让当前的映射文件支持二级缓存(在UserDao.xml中配置)

标签表示当前这个mapper 映射将使用二级缓存,区分的标准就看mapper 的namespace 值。

1
2
3
<mapper namespace="com.cyannote.dao.UserDao">
<cache/>
</mapper>

​ 第三步:让当前的操作支持二级缓存(在select标签中配置)

​ 将UserDao.xml 映射文件中的

注意:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。

1
2
3
4
<!-- 根据id查询用户 -->
<select id="findById" parameterType="INT" resultType="com.cyannote.domain.User" useCache="true">
select * from user where id = #{uid}
</select>

​ 二级缓存结构图

img

​ 经过测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二次查询时,我们发现并没有对数据库发出sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。

​ 但是查询到的2个对象不一致,原理是二级缓存中存放的是对象的散装数据,每次查询的时候需要重新封装实体对象。

考虑二级缓存的应用场景:

​ 适应放置到二级缓存的数据:经常不会发生变化的数据,例如地区编码

​ 不适合放置到二级缓存的数据:经常变化的数据, 财务数据

6.mybatis注解开发

加载注解依赖(pom.xml)

1
2
3
4
5
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>

配置映射(SqlMapConfig.xml)

1
2
3
4
<!-- 指定带有注解的dao接口所在位置 -->
<mappers>
<mapper class="com.cyannote.dao.UserDao"></mapper>
</mappers>

6.1 mybatis的注解说明

​ @Insert:实现新增

​ @Update:实现更新

​ @Delete:实现删除

​ @Select:实现查询

​ @Result:实现结果集封装

​ @Results:可以与@Result一起使用,封装多个结果集

​ @One:实现一对一结果集封装

​ @Many:实现一对多结果集封装

​ @SelectProvider: 实现动态SQL映射

6.2 使用注解开发

使用注解进行CRUD操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//    查询所有
@Select(value = "select * from account")
List<Account> findAll();

// 增加一条记录
@Insert(value = "insert into account (uid,money) values (#{uid},#{money})")
void save(Account account);

// 删除一条记录
@Delete(value = "delete from account where id = #{id}")
void delete(int id);

// 修改一条记录
@Update(value = "update account set uid = #{uid},money = #{money} where id= #{id}")
void update(Account account);

使用注解实现复杂关系映射开发

@one:多对一和一对一

1
2
3
4
5
6
7
8
@Select(value = "SELECT * from account")
@Results(id = "AccountUser", value = {
@Result(id = true, property = "id", column = "id"),
@Result(property = "uid", column = "uid"),
@Result(property = "money", column = "money"),
@Result(property = "user", column = "uid", one = @One(select = "com.cyannote.dao.UserDao.findByUId"))
})
List<Account> findAccountUser();

@many:一对多和多对多

1
2
3
4
5
6
7
8
9
10
@Select(value = "select * from user")
@Results(id = "UserAccount",value = {
@Result(id = true , property = "id" , column = "id"),
@Result(property = "username",column = "username"),
@Result(property = "sex" , column = "sex"),
@Result(property = "birthday" , column = "birthday"),
@Result(property = "address" , column = "address"),
@Result(property = "accounts",column = "id",many = @Many(select = "com.cyannote.dao.AccountDao.findById"))
})
List<User> findUserAccount();

使用联合查询的SQL语句

1
2
3
4
5
6
7
8
9
10
11
12
@Select(value = "SELECT u.*,a.id aid,a.UID,a.MONEY FROM USER u,account a WHERE a.uid = u.id")
@Results(id = "AccountUser2",value = {
@Result(id = true,property = "id",column = "aid"),
@Result(property = "uid",column = "uid"),
@Result(property = "money",column = "money"),
@Result(property = "user.id",column = "id"),
@Result(property = "user.username",column = "username"),
@Result(property = "user.sex",column = "sex"),
@Result(property = "user.birthday",column = "birthday"),
@Result(property = "user.address",column = "address"),
})
List<Account> findAccountUser2();

使用注解配置二级缓存

第一步:配置SqlMapConfig.xml

1
2
3
4
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>

第二步:配置UserDao.xml

1
2
3
@CacheNamespace(blocking = true) // 使用二级缓存
public interface UserDao {
}

MySql单表查询

MySQL单表查询

学习目标

  • 理解数据库和关系型数据库的概念

  • 独立完成数据库的安装

  • 理解关系型数据库的存储结构

  • 独立完成对数据库的添加,修改,删除,查询操作

  • 独立完成对数据库表的添加,修改,删除,查询操作

  • 独立完成对数据库表记录的添加,修改,删除,查询操作

1 MySQL的概述

1.1 数据库的概述

什么是数据库

数据库就是一个文件系统,通过标准的SQL语句获取数据

1.2 MySQL的概述

什么是MySQL数据库

1558492939657

1.3关系型数据库概述

什么是关系型数据库

关系型数据库存放的是实体之间的关系

1558493010920

常见的关系型数据库

  • MySQL

  • Oracle

  • SQLServer

  • DB2

  • SyBase

2 MySQL的数据存储方式的概述

2.1 服务器的概念

什么是服务器

服务器要从硬件和软件两个方面来说:

  • 硬件:指的就是一台计算机。

  • 软件:需要在这台电脑上安装数据库服务器。

2.2 MySQL数据库存储的方式

存储方式的描述

1558493046345

一台数据库服务器中会创建很多数据库(一个项目,会创建一个数据库)。在数据库中会创建很多张表(一个实体会创建一个表)。在表中会有很多记录(一个对象的实例会添加一条新的记录)。

3 SQL的概述

3.1 SQL的概念

什么是SQL

1558493065295

3.2 SQL的分类

DDL:数据定义语言

create,drop,alter..

DCL:数据控制语言

grant,if…

DML:数据操纵语言

insert,update,delete…

DQL:数据查询语言

select

4 使用SQL

4.1 SQL对数据库进行操作

创建数据库

  • 语法:

    • create database 数据库名称 [character set 字符集 collate 字符集校对规则];

查看数据库

  • 语法:

    • 查看数据库服务器中所有的数据库:show databases;
  • 查看某个数据库的定义信息: show create database 数据库名称;

修改数据库

  • 语法:

    • alter database 数据库名称 character set 字符集 collate 校对规则;

删除数据库

  • 语法:

    • drop database 数据库名称;

其他数据库操作

  • 切换数据库:use 数据库名称;

  • 查看当前正在使用的数据库:

4.2 SQL对数据库表进行操作

SQL创建表

  • 语法:

    • create table 表名称(字段名称 字段类型(长度) 约束,字段名称 字段类型(长度) 约束…);
  • 字段类型

    • 一个实体对应一个表,一个实体属性对应表的一个字段。

Java中的类型 MySQL中的类型

byte/short/int/long tinyint/smallint/int/bigint

float float

double double

boolean bit

char/String char和varchar类型

char和varchar的区别:

* char代表是固定长度的字符或字符串。

* 定义类型char(8),向这个字段存入字符串hello,那么数据库使用三个空格将其补全。

* varchar代表的是可变长度的字符串。

* 定义类型varchar(8), 向这个字段存入字符串hello,那么存入到数据库的就是hello。

Date date/time/datetime/timestamp

datetime和timestamp区别

* datetime就是既有日期又有时间的日期类型,如果没有向这个字段中存值,数据库使用null存入到数据库中

* timestamp也是既有日期又有时间的日期类型,如果没有向这个字段中存值,数据库使用当前的系统时间存入到数据库中。

File BLOB/TEXT

  • 约束

    • 约束作用:保证数据的完整性

    • 单表约束分类:

      • 主键约束:primary key 主键约束默认就是唯一 非空的

      • 唯一约束:unique

      • 非空约束:not null

  • 建表语句:

create database web_test1;

use web_test1;

create table user(

id int primary key auto_increment,

username varchar(20) unique,

password varchar(20) not null,

age int,

birthday date

);

SQL查看表

  • 查看某个数据库下的所有的表

  • 语法:show tables;

  • 查看某个表的结构信息

  • 语法:desc 表名;

SQL删除表

  • 删除表

    • 语法:drop table 表名;

{width=”4.134722222222222in” height=”0.8847222222222222in”}

SQL修改表

  • 修改表:添加列

    • alter table 表名 add 列名 类型(长度) 约束;
  • 修改表:修改列类型,长度和约束

    • alter table 表名 modify 列名 类型(长度) 约束;
  • 修改表:删除列

    • alter table 表名 drop 列名;
  • 修改表:修改列名称

    • alter table 表名 change 旧列名 新列名 类型(长度) 约束;
  • 修改表:修改表名

    • rename table 表名 to 新的表名;
  • 修改表:修改表的字符集

    • alter table 表名 character set 字符集;

4.3 SQL对数据库表的记录进行操作(重点)

SQL添加表的记录

  • 语法:

    • 向表中插入某些列:insert into 表名 (列名1,列名2,列名3…) values (值1,值2,值3…)

    • 向表中插入所有列:insert into 表名 values (值1,值2,值3…);

  • 注意事项

    • 1.值的类型与数据库中表列的类型一致。

    • 2.值的顺序与数据库中表列的顺序一致。

    • 3.值的最大长度不能超过列设置最大长度。

    • 4.值的类型是字符串或者是日期类型,使用单引号引起来。

  • 添加记录

    • 添加某几列

insert into user (id,username,password) values (null,'aaa','123');

  • 添加所有列

insert into user values (null,'bbb','123',23,'1993-09-01');

  • 添加中文记录

insert into user values (null,'张三','123',23,'1993-09-01');

直接向数据库中插入中文记录会出现错误!!!

解决方法:

show variables like '%character%'; --查看数据库中与字符集相关参数:

需要将MySQL数据库服务器中的客户端部分的字符集改为gbk。

找到MySQL的安装路径:my.ini文件,修改文件中[client]下的字符集

* 重新启动MySQL的服务器

services.msc

SQL修改表的记录

  • 语法:

    • update 表名 set 列名=值,列名=值 [where 条件];
  • 注意事项

    • 1.值的类型与列的类型一致。

    • 2.值的最大长度不能超过列设置的最大长度。

    • 3.字符串类型和日期类型添加单引号。

  • 修改某一列的所有值

update user set password = 'abc';

  • 按条件修改数据

update user set password = 'xyz' where username = 'bbb';

  • 按条件修改多个列

update user set password='123',age=34 where username='aaa';

SQL删除表的记录

  • 语法:

    • delete from 表名 [where 条件];
  • 注意事项

    • 1.删除表的记录,指的是删除表中的一行记录。

    • 2.删除如果没有条件,默认是删除表中的所有记录。

  • 删除某一条记录

delete from user where id = 2;

  • 删除表中的所有记录

delete from user;

  • 删除表中的记录有两种做法:

    • delete from user;

      • 删除所有记录,属于DML语句,一条记录一条记录删除。事务可以作用在DML语句上的
    • truncate table user;

      • 删除所有记录,属于DDL语句,将表删除,然后重新创建一个结构一样的表。事务不能控制DDL的

SQL查看表的记录(重点)

  • 基本查询

    • 语法:select [distinct] *|列名 from 表 [条件];

    • 环境的准备:

create table exam(

id int primary key auto_increment,

name varchar(20),

english int,

chinese int,

math int

);

insert into exam values (null,'张三',85,74,91);

insert into exam values (null,'李四',95,90,83);

insert into exam values (null,'王五',85,84,59);

insert into exam values (null,'赵六',75,79,76);

insert into exam values (null,'田七',69,63,98);

insert into exam values (null,'李老八',89,90,83);

  • 查询所有学生考试成绩信息

select * from exam;

  • 查询所有学生的姓名和英语成绩

select name,english from exam;

  • 查询英语成绩信息(不显示重复的值)

select distinct english from exam;

  • 查看学生姓名和学生的总成绩

select name,english+chinese+math from exam;

!

  • 别名查询

select name,english+chinese+math as sum from exam;

  • 条件查询

    • 使用where子句

      • > , < , >= , <= , <> ,=

      • like:模糊查询

      • in:范围查询

      • 条件关联:and , or ,not

    • 查询李四学生的成绩:

select * from exam where name = '李四';

  • 查询名称叫李四学生并且英文大于90分

select * from exam where name = '李四' and english > 90;

  • 查询姓李的学生的信息

like可以进行模糊查询,在like子句中可以使用_或者%作为占位符。_只能代表一个字符,而%可以代表任意个字符。

* like ‘李_‘ :名字中必须是两个字,而且是姓李的。

* like ‘李%’ :名字中姓李的学生,李子后可以是1个或任意个字符。

* like ‘%四’ :名字中以四结尾的。

* like ‘%王%’ :只要名称中包含这个字就可以。

select * from exam where name like '李%';

  • 查询英语成绩是69,75,89学生的信息

select * from exam where english in (69,75,89);

  • 排序查询

    • 使用order by 字段名称 asc/desc;

    • 查询学生信息,并且按照语文成绩进行排序:

select * from exam order by chinese;

  • 查询学生信息,并且按照语文成绩倒序排序:

select * from exam order by chinese desc;

  • 查询学生信息,先按照语文成绩进行倒序排序,如果成绩相同再按照英语成绩升序排序

select * from exam order by chinese desc,english asc;

  • 查询姓李的学生的信息,按照英语成绩降序排序

select * from exam where name like '李%' order by english desc;

  • 分组统计查询

    • 聚合函数使用

      • sum();

        • 获取所有学生的英语成绩的总和:

select sum(english) from exam;

  • 获取所有学生的英语成绩和数学成绩总和:

select sum(english),sum(math) from exam;

  • 查询姓李的学生的英语成绩的总和

select sum(english) from exam where name like '李%';

  • 查询所有学生各科的总成绩:

select sum(english)+sum(chinese)+sum(math) from exam;

select sum(english+chinese+math) from exam;

与上面的语句有什么不同?

* 上面的语句是按照列的方式统计,英语成绩总和+语文成绩总和+数学成绩总和。

* 下面的语句先计算英语+数学+语文然后再求和。

* 使用ifnull的函数

  • count();

    • 获得学生的总数

select count(*) from exam;

  • 获得姓李的学生的个数

select count(*) from exam where name like '李%';

  • max();

    • 获得数学成绩的最高分:

select max(math) from exam;

  • min();

    • 获得语文成绩的最小值

select min(chinese) from exam;

  • avg();

    • 获取语文成绩的平均值

select avg(chinese) from exam;

  • 分组查询

    • 语法:使用group by 字段名称;

    • 环境准备

create table orderitem(

id int primary key auto_increment,

product varchar(20),

price double

);

insert into orderitem values (null,'电视机',2999);

insert into orderitem values (null,'电视机',2999);

insert into orderitem values (null,'洗衣机',1000);

insert into orderitem values (null,'洗衣机',1000);

insert into orderitem values (null,'洗衣机',1000);

insert into orderitem values (null,'冰箱',3999);

insert into orderitem values (null,'冰箱',3999);

insert into orderitem values (null,'空调',1999);

  • 按商品名称统计,每类商品所购买的个数:

select product,count(*) from orderitem group by product;

  • 按商品名称统计,每类商品所花费的总金额:

select product,sum(price) from orderitem group by product;

  • 按商品名称统计,统计每类商品花费的总金额在5000元以上的商品

***** where的子句后面不能跟着聚合函数。如果现在使用带有聚合函数的条件过滤(分组后条件过滤)需要使用一个关键字having

select product,sum(price) from orderitem group by product having sum(price) > 5000;

  • 按商品名称统计,统计每类商品花费的总金额在5000元以上的商品,并且按照总金额升序排序

select product,sum(price) from orderitem group by product having sum(price) > 5000 order by sum(price) asc;

  • 总结

    • S(select)… F(from)…W(where)…G(group by)…H(having)…O(order by);

Redis

Redis

增加数据

String : set key value

设定该Key持有指定的字符串Value

List : lpush key value [value …]

添加键和对应的多个值

hash : hset key field value

为指定的Key设定Field/Value对

Set: sadd key member [member …]

添加键和对应的值

sortedSet: zadd key score member [score] [member]

添加成员

获取数据

String: GET key 获取指定Key的Value

List : lrange key start stop 查询该键对应的值

Hash: HMGET key field [field …] 获取和参数中指定Fields关联的一组Values。

 HGETALL key 获取该键包含的所有Field/Value
 HKEYS key 获取指定Key的所有Fields名
 HVALS key 返回指定Key的所有Values名。

Set: SMEMBERS key 获取与该Key关联的Set中所有的成员

SortedSet:  ZRANGE key start stop [WITHSCORES] 根据索引获取成员
 ZRANGEBYSCORE key min max 根据分数获取成员

删除数据

String: del key 删除key

List: lrem key count value 删除指定键中前count个值等于value的元素

Hash: hdel key field [field …] 从指定Key的Hashes Value中删除参数中指定的多个字段,如果不存在的字段将被忽略。

Set: SREM key member [member …] 从与Key关联的Set中删除参数中指定的成员

SortedSet: ZREM key member [member …] 删除成员

其他:

 SDIFF key [key …] 获取第一个key和后面所有key中不同的部分
 SINTER key [key …] 获取所有Keys关联的Sets中成员的交集
 SUNION key [key …] 获取所有Keys关联的Sets中成员的并集

 KEYS pattern 获取所有匹配pattern参数的Keys
 DEL key [key …] 删除指定的keys
 EXPIRE key seconds 为参数中指定的Key设定超时的秒数,在超过该时间后,Key被自动的删除
 PERSIST key 取消key的过期时间
 TTL key 获取该键所剩的超时描述

Searcher搜索引擎

Search

1. 搜索引擎

1.1 什么是搜索引擎

​ 搜索引擎是指根据一定的策略、运用特定的计算机程序从互联网上搜集信息,在对信息进行组织和处理后,为用户提供检索服务,将用户检索相关的信息展示给用户的系统。例如: 百度 谷歌

1.2 搜索引擎基本的运行原理

1563674656348

1.3 原始数据库查询的缺陷

  • 1) 慢, 当数据库中的数据量很庞大的时候, 整个的查询效率非常低, 无法及时返回内容
  • 2) 搜索效果比较差, 只能根据用户输入的完整关键字的进行首尾的模糊匹配
  • 3) 如果用户输入的关键字出现错别字, 或者多输入了内容, 可能就导致结果远离用户期望的内容

1.4 倒排索引技术 (理解)

​ 倒排索引, 又称为反向索引: 以字或者词,甚至是一句话一段话作为一个关键字进行索引, 每一个关键字都会对应着一个记录项, 记录项中记录了这个关键字出现在那些文档中, 已经在此文档的什么位置上

为什么说倒排索引可以提升查询的效率和精准度呢?

​ 倒排索引, 是将数据提前按照格式分词放好,建立索引, 当用户进行搜索, 将用户的关键字进行分词, 然后根据分词后的单词到索引库中寻找对应词条,根据词条, 查到对应所在的文档位置, 将其文档内容直接获取即可

整个查询过程 基于索引查询

2. Lucene

​ Lucene是Apache提供的一个开源的全文检索引擎工具包, 其本质就是一堆jar包而已, 而非一个完整的搜索引擎, 但是我们可以通过Lucene来构建一个搜索引擎

官方网址: http://lucene.apache.org/

2.1 使用Lucene如何构建索引

2.1.1 第一步: 导入相关的jar包(pom依赖)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queries</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-test-framework</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>4.10.2</version>
</dependency>

2.1.2 第二步: 书写写入索引的代码

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 索引写入相关的内容
public class IndexWriterTest {

public static void main(String[] args) throws Exception{
//1. 创建 lucene用于写入索引的核心类: IndexWriter
Directory d = FSDirectory.open(new File("F:\\index"));
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST,analyzer);

IndexWriter indexWriter = new IndexWriter(d,conf);

//2. 添加原始文档数据
Document doc = new Document();
doc.add(new LongField("id",1, Field.Store.YES));
doc.add(new StringField("title","母爱满满!哈里王子梅根王妃母亲节晒宝宝脚丫", Field.Store.YES));
doc.add(new TextField("content","哈里王子梅根王妃在社交媒体上发图文", Field.Store.YES));

indexWriter.addDocument(doc);

//3. 提交文档数据
indexWriter.commit();

//4. 释放资源
indexWriter.close();

}
}

2.1.3 Lucene的索引修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 索引的修改
// 修改后的数据都在文档的最后面, 先删除, 后添加
@Test
public void updateIndexTest() throws Exception{
//1. 创建indexWriter对象
Directory d = FSDirectory.open(new File("F:\\index"));
IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST,new IKAnalyzer());
IndexWriter indexWriter = new IndexWriter(d,conf);

//2. 创建一个修改后文档对象

Document doc = new Document();
doc.add(new StringField("id","6", Field.Store.YES));
doc.add(new TextField("title","这是修改后的数据", Field.Store.YES));

//3. 执行修改 : 在执行修改的时候, 不要使用id字段, 因为id字段在书写的时候使用的Long类型, long类型是要分词的
// 如果想根据id来修改数据, 需要将id的类型更改为StringField(不分词)
indexWriter.updateDocument(new Term("title","杭州"),doc);

//4. 提交修改
indexWriter.commit();

//5. 释放资源
indexWriter.close();
}

2.1.4 Lucene 的索引删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 索引的删除

@Test
public void deleteIndexTest() throws Exception{
//1. 创建indexWriter对象 : 增删改
Directory d = FSDirectory.open(new File("F:\\index"));
IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST,new IKAnalyzer());
IndexWriter indexWriter = new IndexWriter(d,conf);

//2. 执行删除索引操作
//indexWriter.deleteAll(); //删除所有
indexWriter.deleteDocuments(new Term("title","修改")); // 根据词条删除
//indexWriter.deleteDocuments(query); 根据条件删除

//3. 提交
indexWriter.commit();

//4. 释放资源
indexWriter.close();
}

2.1.5 查询索引

查询解析器查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 //索引查询的入门代码
public class IndexSearcherTest {


public static void main(String[] args) throws Exception {
//1. 创建lucene用于查询索引核心对象
IndexReader r = DirectoryReader.open(FSDirectory.open(new File("F:\\index")));
IndexSearcher indexSearcher = new IndexSearcher(r);

//2. 添加查询的条件
//2.1 通过查询解析器的方式来获取query对象
QueryParser queryParser = new QueryParser("content",new IKAnalyzer());
// 参数: 表示的是用户输入的内容
Query query = queryParser.parse("蓝瘦香菇吊炸天");
//3. 执行查询
// 参数1: 查询的条件
//参数2: 查询前几个
// 返回值: 结果集
// 主要包含二部分的内容: 1) 查询的结果集的得分数组 2) 查询的总条数
TopDocs topDocs = indexSearcher.search(query, Integer.MAX_VALUE);

//4. 获取数据
ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 得分文档的数组
int totalHits = topDocs.totalHits; //查询的总条数
// ScoreDoc :得分文档对象, 包含二部分内容: 1) 文档的id 2) 文档的得分
for (ScoreDoc scoreDoc : scoreDocs) {
float score = scoreDoc.score; //得分
int docId = scoreDoc.doc; //文档的id

Document document = indexSearcher.doc(docId);
String id = document.get("id");
String title = document.get("title");
String content = document.get("content");

System.out.println("文档的得分为:" + score +" 文档的id:"+id+" 文档的标题:"+title+" 文档的内容:"+ content);
}
}
}

组合查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 多样化查询:  BooleanQuery  组合查询
// 组合查询本身自己是没有任何的条件的, 将其他的条件组合在一块, 共同作用于着整个的查询

/**
* MUST : 必须, 表示查询出来的结果必须是这个条件中的数据
* MUST_NOT: 不必须 , 表示查询出来的结果必须不能包含这个条件中的数据
* SHOULD: 应该 , 表示查询出来的结果, 如果有这个条件中数据就显示, 没有就不显示, 对整个结果没有太大影响
*/
@Test
public void booleanQueryTest()throws Exception{
BooleanQuery query = new BooleanQuery();

NumericRangeQuery numericRangeQuery = NumericRangeQuery.newLongRange("id",2L,3L,true,true);
query.add(numericRangeQuery, BooleanClause.Occur.MUST);

TermQuery termQuery = new TermQuery(new Term("title","lucene"));
query.add(termQuery, BooleanClause.Occur.MUST);

publicQuery(query);
}

3. Solr

1557968128127

​ solr是一个企业级的搜索应用服务器, 如果想要连接solr需要通过发送http请求方法 , solr是基于lucene来来实现的.

3.1 solr的目录介绍

  • solr的根目录

  • example的目录

3.2 solr服务的启动

3.2.1 solr的启动一: start.jar(了解)

启动步骤:

  • 1) 打开cmd窗口
  • 2) 切换目录到solr的example的目录下
  • 3) 执行 java -jar start.jar

注意: 由于jar包已经内置了一个web服务器(jetty) , 其默认的访问的为8983

3.2.2 solr的启动二: solr.war

1
如果想要部署solr.war, 需要先准备一个tomcat, 建议准备一个全新的tomcat, 不要使用原来已经添加其他项目的tomcat, 将这个全新的tomcat放置在一个没有中文和空格的目录下, 建议放置在solr的目录下

需要保证: tomcat是可以正常启动的, 否则不要往下执行, 先解决tomcat的问题.

​ 一般出现的问题闪退的原因: JAVA_HOME配置有问题

启动步骤:

  • 1) 首先打开example目录下的webapps目录下
  • 2) 拷贝此目录下的solr.war , 将其复制到tomcat的webapps目录下

  • 3) 启动tomcat, 将war包进行解压, 然后关闭tomcat并将war包删除或者更改后缀名即可
    • 将其原有war包更改的主要目的是防止再次解压, 导致原有的设置失效

  • 4) 将资料中<<tomcat运行solr所需要的jar包>>复制到tomcat中solr的web-inf下的lib目录中 ,classes目录复制到web-inf下

  • 5)将example中solr的目录建议复制到和tomcat同级的目录下(方便管理)
    • 复制后, 建议将这个索引库目录名称更改为solr-home

  • 6) 打开tomcat的bin目录找到Catalina.bat文件, 将其右键打开
  • 7) 将下列参数设置到此文件中即可
    • 参数 : set “JAVA_OPTS=-Dsolr.solr.home=目录位置”
    • 注意: 目录位置就是刚刚复制过来的solr的目录

  • 8) 启动tomcat,访问localhost:8080/solr即可

1557970335432

3.3 solr的管理界面

  • 仪表盘

  • 日志窗口: 记录solr在启动过程中和启动后执行过程中的执行信息

解决警告信息:

​ 第一步: 将solr的安装包中依赖包的两个目录, 复制到solr的索引库中

​ 第二步: 打开索引库中的collection1中conf目录, 将solrconfig.xml右键打开, 去掉两个../即可

  • core 窗口: 用于配置solr的索引库
    • solr中支持配置和管理多个索引库, 就像数据库中有多个database是一样的

如何配置多个索引库呢?

​ 简单方案: 将collection1复制一个,然后删除其data文件夹并修改core.properties配置文件即可

  • core selector: core的选择器, 用来针对不同core进行操作的

针对core selector的详细讲解:

3.3.1 使用solr的管理界面进行添加索引

3.3.2 使用solr的管理界面进行查询索引

在进行查询的时候, 都是使用代码的形式进行查询索引的, 然后展示给用户的系统, 而solr提供的管理界面, 主要是为了方便程序员对solr进行管理和测试的, 比如query: 在代码中编写查询的代码后, 我们并不知道查询后可能会展示什么内容, 或者并不知道展示的内容是否正确, 使用管理界面测试一下, 看一下结果和代码的结果是否一样

3.4 solr的配置文件

core.properties: 设置索引库的名称

3.4.1 solrConfig.xml : solr的核心配置文件(了解)

​ solrconfig.xml 配置文件主要定义了 solr 的一些处理规则,包括索引数据的存放 位置,更新,删除,查询的一些规则配置, solr相关的优化的时候, 需要使用这个配置文件。

​ 一般此文件不需要进行修改, 采取默认即可

3.4.2 schema.xml: solr约束文件:

​ Solr中会提前对文档中的字段进行定义,并且在schema.xml中对这些字段的属性进行约束,例如:字段数据类型、字段是否索引、是否存储、是否分词等等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<?xml version="1.0" encoding="UTF-8" ?>

<schema name="example" version="1.5">

<!-- 不能删除
-->
<field name="_version_" type="long" indexed="true" stored="true"/>

<field name="_root_" type="string" indexed="true" stored="false"/>

<!-- 不删除
-->

<!--
field标签: 字段标签,
作用: 用来定义索引库中的字段的, 在solr中字段需要提前定义好, 而在lucene中手动定义的
name : 字段的名称
type : 字段的类型
indexed : 是否(保存)分词
stored : 是否保存原始数据
required : 是否必须存在
multiValued : 是否是多值的, 如果为true, 表示当前这个字段是一个数组

id字段默认是必须存在的, 也就是说在创建document的时候, 在文档中必须要有id字段
id表示的文档的唯一标识, 在solr中需要程序员自己进行维护, 在lucene中是由lucene自己维护的

-->

<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />


<field name="name" type="string" indexed="true" stored="true"/>

<field name="title" type="string" indexed="true" stored="true" multiValued="true"/>

<field name="content" type="string" indexed="true" stored="true" multiValued="false"/>

<field name="text" type="string" indexed="true" stored="false" multiValued="true"/>


<!--
dynamicField : 动态字段(动态域)
作用 : 在solr中需要提前定义一些字段的, 但是不能够保证将所有的字段都提前的定义好,动态域中将可以帮助我们动态的生成一些字段

假设, 在solr启动后, 想要使用docurl的字段, 但是这个字段并没有在schema文件中定义, 如果做?
1) 将solr停机, 然后更改schema的文件, 添加一个新的字段
但是服务器一般启动后, 是不允许随意的停机的
2) 使用动态域字段 : docurl_i
-->
<dynamicField name="*_i" type="int" indexed="true" stored="true"/>
<dynamicField name="*_is" type="int" indexed="true" stored="true" multiValued="true"/>

<!--
uniqueKey : 表示的文档的唯一主键是那个字段 , 默认是id字段, 当然也可以进行修改
设置的这个唯一的字段, 类型必须是string, 字段是必须存在的, 不能是多值
-->
<uniqueKey>id</uniqueKey>

<!--
copyField : 复制字段(复制域)
作用 : 主要是用于查询阶段使用的, 实现查询一个字段相当于查询多个字段
source : 来源
dest : 目的地

以下这两个标签描述的意思:
将content 和 title字段中的索引数据复制一份出来, 放置到text的字段中, 这样当查询text字段的时候, 就相当于查询title和content
复制域中目的地的字段必须是一个多值的字段
-->
<copyField source="content" dest="text"/>
<copyField source="title" dest="text"/>

<!--
fieldType : 字段类型,
作用: 定义字段类型的
-->
<fieldType name="string" class="solr.StrField" sortMissingLast="true" />

<fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>


<fieldType name="int" class="solr.TrieIntField" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="float" class="solr.TrieFloatField" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" positionIncrementGap="0"/>

<fieldType name="date" class="solr.TrieDateField" precisionStep="0" positionIncrementGap="0"/>

</schema>

要求: 能够自己手动的添加字段即可, 能够根据需要添加一个复制域的标签

例如: 想要添加一个 docurl的字段, 这个字段的类型是string, 字段的需要进行分词的, 原始也需要保存, 不是一个多值的字段

例如: 娱乐头条中, 当查询新闻数据的时候, 一般需要根据那些字段来查询?

3.4.3 引入ik分词器

  • 第一步: 导入ik相关的依赖包
    • 将三个文件放置在tomcat>webapps>solr>WEB-INF>lib下(此步骤在部署solr到tomcat中的时候, 就已经导入了)

  • 第二步: 导入ik相关的配置文件(ik配置文件, 扩展词典和停止词典)
    • 将三个文件放置在tomcat>webapps>solr>WEB-INF>classes下(此步骤, 在部署solr到tomcat中的时候, 已经导入)

  • 第三步, 在schema.xml配置文件中自定义一个字段类型, 引入ik分词器
1
2
3
<fieldType name="text_ik" class="solr.TextField">
<analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>

  • 第四步: 为对应的字段设置为text_ik类型即可

3.5. solr的客户端操作: solrj

​ solrj是Apache官方提供的一套java开发的, 用于操作solr服务的API, 通过这套API可以让java程序与solr服务进行交互, 让java程序可以直接操作solr服务进行增删改查

solrj的官网网址: https://wiki.apache.org/solr/Solrj

3.5.1 solr的基本入门程序

  • 第一步: 导入相关的jar包
1
2
3
4
5
6
7
8
9
10
11
	<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>4.10.2</version>
</dependency>
<!--日志的包, solrj执行需要一个日志包-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging-api</artifactId>
<version>1.1</version>
</dependency>
  • 编写入门程序(写入索引)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 写入索引的入门代码(原生写入)
@Test
public void indexWriterTest01() throws Exception{

//1. 创建solrj连接solr的远程服务对象 : #表示的页面的路径 不带#号才是访问的接口路径
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr/collection1");
//2. 添加文档数据
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id",1);
doc.addField("title","连休三天!端午节放假通知来了");
doc.addField("content","请广大市民提前安排好工作生活,节日期间注意安全,度过一个平安、祥和的节日假期。");

solrServer.add(doc);

//3. 提交数据
solrServer.commit();


}

3.5.2 使用solrj写入索引(使用javabean进行写入)

一个标准的javaBean都具备什么条件?

  • 1) 成员属性必须私有化
  • 2) 必须为成员属性提供get 和 set方法
  • 3) 必须提供无参的构造方法
  • 4) 必须要实现序列化接口: 只要实现可这个接口, 那么这个对象支持网络io操作及持久化到磁盘的操作

pojo类:

1
2
3
4
5
6
7
public class News implements Serializable {
private String id;
private String title;
private String content;
private String docurl;
// 省略了 get 和 set方法 和 toString的方法
}
  • 代码的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 直接写入一个javaBean对象
@Test
public void indexWriterTest03() throws Exception {

//1. 创建solrj连接solr的服务对象
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr/collection1");

//2. 添加文档数据
News news = new News("8","国务院定了!11月底前在全国全面实施“携号转网”","国务院总理李克强5月14日主持召开国务院常务会议,部署进一步推动网络提速降费,发挥扩内需稳就业惠民生多重效应","http://baijiahao.baidu.com/s?id=1633615346318630206");

solrServer.addBean(news);

//3. 提交数据
solrServer.commit();
}

1557980015574

1
2
3
4
出现错误的原因: solr并不知道在pojo中那个字段是solr的字段, 所以solr报错显示没有定义字段错误
解决方案: 告诉solr那个是你的字段
添加一个字段的注解即可 : @Field
如果字段的值 和 solr中字段不匹配, 可以在注解后面添加指定的字段值, 标识将pojo中属性放置到这个字段中

1557980446558

3.5.3 删除索引

修改索引: 只要保证id一致, 就是修改索引的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 删除索引
@Test
public void delIndexTest04() throws Exception{
//1. 创建solrj连接solr的而服务对象
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr/collection1");

//2. 添加删除的条件
//solrServer.deleteById("change.me"); 根据id删除
//solrServer.deleteByQuery("*:*"); //删除所有
solrServer.deleteByQuery("id:1"); //删除所有
//3. 执行删除

solrServer.commit();
}

3.5.4 查询操作

3.5.4.1 查询入门代码(原始)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 1) 查询索引的入门代码(原生)
@Test
public void indexSearcherTest01() throws Exception{
//1. 创建solrj连接solr的服务对象
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr/collection1");

//2. 封装查询的条件
SolrQuery query = new SolrQuery("*:*");

//3. 执行查询
QueryResponse response = solrServer.query(query);
//4. 获取数据
SolrDocumentList documentList = response.getResults();

for (SolrDocument document : documentList) {
String id = (String) document.get("id");
String content = (String) document.get("content");
ArrayList<String> title = (ArrayList<String> ) document.get("title");
String docurl_i = (String) document.get("docurl_i");

System.out.println(id+" "+title+" "+content+" "+ docurl_i);

}

}
3.5.4.2 返回javaBean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//2.  查询索引, 返回javaBean对象
@Test
public void indexSearcherTest02() throws Exception{
//1. 创建solrj 连接solr的服务对象
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr/collection1");

//2. 封装查询条件
SolrQuery solrQuery = new SolrQuery("*:*");

//3. 执行查询
QueryResponse response = solrServer.query(solrQuery);

//4. 获取数据 : 采用的第三种方式来解决数据无法转换的问题
ArrayList<News> newsArrayList = new ArrayList<News>();
SolrDocumentList documentList = response.getResults();
for (SolrDocument document : documentList) {
News news = new News();

String id = (String) document.get("id");
news.setId(id);
String content = (String) document.get("content");
news.setContent(content);
ArrayList<String> title = (ArrayList<String> ) document.get("title");
news.setTitle(title.get(0));
String docurl_i = (String) document.get("docurl_i");
news.setDocurl(docurl_i);

newsArrayList.add(news);
}

System.out.println(newsArrayList);

}

1557990116016

1
2
3
4
5
6
7
原因: 不能将一个集合类型的数据转换成一个字符串

解决方案:
1) schema.xml中配置信息: 将多值的字段更改为单值字段
注意: 一旦将约束文件修改了, 建议将数据全部清空, 重新添加, 所以在添加数据之前, 一定要确认配置文件是没有问题的
2) 将javaBean中指定的字段更改为对应的类型即可
3) 手动人为封装即可(推荐使用)
3.5.4.2 复杂查询
  • 抽取一个公共的查询的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 抽取一个公共的查询的方法
public void publicQuery(SolrQuery query) throws Exception{

//1. 创建 solrj连接solr的服务对象
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr/collection1");

//2. 封装查询的条件

//3. 执行查询

QueryResponse response = solrServer.query(query);


//4. 获取数据
List<News> newsList = response.getBeans(News.class);

for (News news : newsList) {
System.out.println(news);
}

}

在创建SolrQuery时,我们填写的Query语句,可以有以下高级写法:

查询语句中如果有特殊字符,需要转义,可以使用: ” ”

1、匹配所有文档:*:* (通配符?和 * :“*”表示匹配任意字符;“?”表示匹配出现的位置)

2、布尔操作:AND、OR和NOT布尔操作(推荐使用大写,区分普通字段)

3、子表达式查询(子查询):可以使用“()”构造子查询。 比如:(query1 AND query2) OR (query3 AND query4)

4、相似度查询:

(1)默认相似度查询:title:appla~,此时默认编辑距离是2

(2)指定编辑距离的相似度查询:对模糊查询可以设置编辑距离,可选02的整数:title:appla1。

5、范围查询(Range Query):solre支持对数字、日期甚至文本的范围查询,并且两端范围。结束的范围可以使用“*”通配符。

(1)日期范围(ISO-8601 时间GMT):birthday:[1990-01-01T00:00:00.000Z TO 1999-12-31T24:59:99.999Z]

(2)数字:age:[2000 TO *]

(3)文本:content:[a TO a]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 复杂查询:

@Test
public void complexQueryTest() throws Exception {
//1. 通配符查询 : * ?
// SolrQuery query = new SolrQuery("content:luce*");

//2. 相似度查询 和 lucene的模糊查询 最大编辑次数 2次
// 可以在~的后面修改最大的编辑次数范围 0~2 , 如果编写的范围不是这个范围之内的数字, 那么solr修改为默认值 2
// SolrQuery query = new SolrQuery("content:lucene~");

//3. 范围查询 : 对 数字, 文本, 日期进行范围查询
// 格式 : 字段名称 : [min TO MAX]

// 根据id查询, 是文本范围呢? 还是数字范围 ? 是文本范围, 因为id类似是字符串
// 文本范围: 按照字典的顺序进行排列
// 字典顺序 : 1 2 3 4 5 6 7 8 9 10 11 12 13 22 33 44 55
// 按照字典顺序:从小到大 1 10 11 12 13 2 22 3 33 4 44 5 55 6 7 8 9
// 例如: 手机通信录, 微信通信录

// 日期范围: 在solr中对于日期的格式是有明确规定的, 在solr中采用日期的格式为国际通用时间UTC(国际协调时间)格式
// UTC格式 : yyyy-MM-dd'T'HH:mm:ss'Z'
// 国际时间 和 中国时间 是有时间差 : 8个小时, 中国属于东八区 和大于国际时间八个小时
// 所以当向solr中存储一个日期类型的值: 日期的时间差的问题, 建议转换成国内时间
// 如果拿到的时候, 本身就是国内时间值, 采用字符串的形式写入到solr的索引库, 那么solr索引库不会将其-8小时的

// SolrQuery query = new SolrQuery("id:[1 TO 5]");

//4. 布尔操作 和 lucene中BooleanQuery 是类似的
/** SOLR LUCENE
* AND MUST
* NOT MUST_NOT
* OR SHOULD
*/
// 5 子表达式 : 布尔查询中派生出来查询的方式 id:[2 TO 5] AND (content:lucene OR title:5)
SolrQuery query = new SolrQuery("id:[2 TO 5] AND (content:lucene OR title:5) NOT content:3"); // 5 2 3 4 7

publicQuery(query);
}

SolrQuery : 将这个对象可以看做是整个搜索的界面对象

​ 所以整个查询的界面中所有的参数都可以通过SolrQuery来封装查询处理

​ 支持复杂的查询方式

3.6. solr的高级

3.6.1 solr的高亮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// solr的高亮
@Test
public void highlighterTest01() throws Exception{
//1. 创建solrj连接solr的服务对象
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr/collection1");

//2. 封装查询的条件 : SolrQuery
SolrQuery solrQuery = new SolrQuery("content:solr是apache节日期间");
//2.1 高亮的参数
solrQuery.setHighlight(true); //开启高亮
solrQuery.addHighlightField("title");
solrQuery.addHighlightField("content");
solrQuery.setHighlightSimplePre("<font color='red'>");
solrQuery.setHighlightSimplePost("</font>");

//3. 执行查询
QueryResponse response = solrServer.query(solrQuery);

//4. 获取数据
List<News> newsList = response.getBeans(News.class);


//5. 获取高亮
// 最外层的MAP: KEY 高亮的文档的id值
// 内层的map : key 表示的高亮的字段
// list集合: 存储对应高亮字段的高亮的内容, 一般来说list集合中只有一个值
Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();

for (News news : newsList) {

String id = news.getId();
Map<String, List<String>> listMap = highlighting.get(id);
List<String> list = listMap.get("title");
if(list != null && list.size()>0){
// 表示有title的高亮
news.setTitle(list.get(0));
}

list = listMap.get("content");
if(list != null && list.size()>0){
// 表示有title的高亮
news.setContent(list.get(0));
}

System.out.println(news);
}


}

3.6.2 solr的排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Test
public void sortTest02() throws Exception{
//1. 创建solrj连接solr的服务对象
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr/collection1");

//2. 封装查询的条件 : SolrQuery
SolrQuery solrQuery = new SolrQuery("content:solr是apache节日期间");
//2.1 高亮的参数
solrQuery.setHighlight(true); //开启高亮
solrQuery.addHighlightField("title");
solrQuery.addHighlightField("content");
solrQuery.setHighlightSimplePre("<font color='red'>");
solrQuery.setHighlightSimplePost("</font>");

//2.2 添加排序的条件
solrQuery.setSort("id", SolrQuery.ORDER.asc);
//3. 执行查询
QueryResponse response = solrServer.query(solrQuery);

//4. 获取数据
List<News> newsList = response.getBeans(News.class);


//5. 获取高亮
// 最外层的MAP: KEY 高亮的文档的id值
// 内层的map : key 表示的高亮的字段
// list集合: 存储对应高亮字段的高亮的内容, 一般来说list集合中只有一个值
Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();

for (News news : newsList) {

String id = news.getId();
Map<String, List<String>> listMap = highlighting.get(id);
List<String> list = listMap.get("title");
if(list != null && list.size()>0){
// 表示有title的高亮
news.setTitle(list.get(0));
}

list = listMap.get("content");
if(list != null && list.size()>0){
// 表示有title的高亮
news.setContent(list.get(0));
}

System.out.println(news);
}


}

3.6.3 solr的分页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// 分页
@Test
public void pageTest03() throws Exception{
int page = 3; //当前页
int pageSize = 2; // 每页的条数

//1. 创建solrj连接solr的服务对象
SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr/collection1");

//2. 封装查询的条件 : SolrQuery
SolrQuery solrQuery = new SolrQuery("content:solr是apache节日期间");
//2.1 高亮的参数
solrQuery.setHighlight(true); //开启高亮
solrQuery.addHighlightField("title");
solrQuery.addHighlightField("content");
solrQuery.setHighlightSimplePre("<font color='red'>");
solrQuery.setHighlightSimplePost("</font>");

//2.2 添加排序的条件
solrQuery.setSort("id", SolrQuery.ORDER.asc);

//2.3 封装分页的条件
solrQuery.setStart( (page-1)*pageSize );
solrQuery.setRows(pageSize);

//3. 执行查询
QueryResponse response = solrServer.query(solrQuery);

//4. 获取数据
List<News> newsList = response.getBeans(News.class);


//5. 获取高亮
// 最外层的MAP: KEY 高亮的文档的id值
// 内层的map : key 表示的高亮的字段
// list集合: 存储对应高亮字段的高亮的内容, 一般来说list集合中只有一个值
Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();

for (News news : newsList) {

String id = news.getId();
Map<String, List<String>> listMap = highlighting.get(id);
List<String> list = listMap.get("title");
if(list != null && list.size()>0){
// 表示有title的高亮
news.setTitle(list.get(0));
}

list = listMap.get("content");
if(list != null && list.size()>0){
// 表示有title的高亮
news.setContent(list.get(0));
}

System.out.println(news);
}


}

总结:

  • 1) solr的基本概念: solr是apache官方提供的一个企业级的全文搜索应用服务器, 连接solr服务器需要发送http请求, solr的底层是lucene
  • 2) solr的部署 :
    • 2.1) solr的下载 : 1) 通过官网(只能下载最新版本) 2) 下载历史的版本 3) 使用资料提供好的
    • 2.2) solr的目录的结构 :
    • 2.3) solr的部署:
      • 2.3.1) 一种采用start.jar 的方式部署(最简单的方式),内置了jetty的服务器(8983)
      • 2.3.2) 采用 solr.war方式进行部署(步骤比较多), 部署在tomcat中 (需要参考文档部署成功)
  • 3) solr的管理界面 : 会操作即可
    • 3.1) 检测某一个字段的分词效果
    • 3.2) 使用管理界面能够添加索引和修改索引
    • 3.3) 使用管理界面可以查询索引数据
    • 3.4) 查看某一个字段在索引库中分词内容
  • 4) solr的配置文件 :
    • 4.1) core.properties: 配置索引库的名称
    • 4.2) solrConfig.xml: 对solr的服务器进行相关的配置
    • 4.3) schema.xml : 对solr的索引库进行相关的配置
  • 5) solr的客户端操作:solrj的操作 ——-会操作, 尽可能不用查看文档内容
    • 使用solrj完成 对solr索引库的CURD操作
    • 使用solrj完成对solr高级的操作: 高亮, 排序, 分页

常见错误:

1563685934033


4.solrCloud

​ SolrCloud(solr 云)是Solr提供的分布式搜索方案,当你需要大规模,容错,分布式索引和检索能力时使用 SolrCloud。当一个系统的索引数据量少的时候是不需要使用SolrCloud,当索引量很大搜索请求并发很高,这时需要使用SolrCloud来满足这些需求。

1solrCloud的结构

​ SolrCloud为了降低单机的处理压力,需要由多台服务器共同来完成索引和搜索任务。实现的思路是将索引数据进行Shard(分片)拆分,每个分片由多台的服务器共同完成,当一个索引或搜索请求过来时会分别从不同的Shard的服务器中操作索引。

​ SolrCloud需要Solr基于Zookeeper部署,Zookeeper是一个集群管理软件,由于SolrCloud需要由多台服务器组成,由zookeeper来进行协调管理。

下图是一个SolrCloud应用的例子:

1533630926299

  • 物理结构:
    • 指定就是三台solr服务器(直白一点就是三个部署了solr的tomcat)
      • 每个tomcat中都有两个core(索引库)
  • 逻辑结构
    • 我们把整个集群看做了一个整体(一个大的索引库), 在这个索引库下有两个分片(也就是把索引库分成两部分), 每一个分片都有三个节点(对应就是物理结果中各个tomcat的索引库),其中一个为主节点,两个备份节点

整体部署的结构图:

1533649208319

2. solr集群中数据的读取及分裂流程

  • 写数据的流程

1558141754746

写入的流程中: 写入索引的节点是每个分片的主节点

请问: 如果能够让分片中数据保存均衡呢?

​ 1) 你一下我一下(轮询)

​ 2) hash取模计算法 : 先对数据进行hash取值, 得到一个int类型的hash值, 除于当前分片的数量, 对其取余: 0~分片的数量-1 , 得到那个结果, 将其保存在对应编号的分片中即可

​ 随着数量量越大, 均匀分配越好

  • 读数据

1558142487391

读数据过程, 不需要master程序参与的, 由slave节点完成读的操作

在solrCloud中, 读写是分离的

  • 副本分裂

1558144177890

如果想要删除一个分片, 必须要将这个分片进行分裂, 因为如果不分裂, 那么这个分片中是有整个索引库中一部分的数据的, 不允许删除

3. solrCloud相关的管理命令

3.1 创建新集群(创建一个索引库)

1
2
3
4
5
6
7
8
9
10
11
12
http://192.168.72.141:8080/solr/admin/collections?action=CREATE&name=collection2&numShards=2&replicationFactor=3&maxShardsPerNode=8&property.schema=schema.xml&property.config=solrconfig.xml


接口参数说明:

action: 表示执行的操作 CREATE 创建
name: 新集群的名称
numShards: 分片数
replicationFactor: 每个分片的节点数
maxShardsPerNode: 设置每个分片的最大节点数, 默认为1
property.schema: 指定使用的schema文件 (注意, 此文件必须在zookeeper上存在)
property.config: 指定使用的solrConfig文件 (注意, 此文件必须在zookeeper上存在)

1533636808020

3.2 删除core命令

会将真实的索引库的数据删除, 这个命令慎用

1
http://192.168.72.141:8080/solr/admin/collections?action=DELETE&name=collection1

3.3 查询所有的Core

1
http://192.168.72.141:8080/solr/admin/collections?action=LIST

3.4 显示core的状态

1
http://192.168.72.141:8080/solr/admin/collections?action=clusterstatus

3.5 分裂shard(扩展容量)

分裂: 就是将某个分片分成两个分片

​ 注意: 一旦分裂后, 虽然原来的分片还可以提供服务, 但是已经不再保存数据, 会将数据保存到新的分裂后的分片

1
2
3
4
http://192.168.72.141:8080/solr/admin/collections?action=SPLITSHARD&collection=collection2&shard=shard1

参数说明:
shard: 指定要分裂的分片

3.6 删除某个分片

只是一个逻辑删除, 不会真正的将分片直接删除的,

如果要将分片数据全部删除, 需要手动一个个删除

注意: 删除的分片必须是已经被分裂的, 或者是已经无法提供服务的

1
http://192.168.72.141:8080/solr/admin/collections?action=DELETESHARD&shard=shard2&collection=collection2

4. 使用solrj连接集群, 完成基本的索引库操作

4.1 导入相关的pom依赖

1
2
3
4
5
6
7
8
9
10
11
12
<dependencies>
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging-api</artifactId>
<version>1.1</version>
</dependency>
</dependencies>

4.2 编写代码,完成CURD

  • 添加(修改)索引
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//1. 添加索引数据 :
@Test
public void indexWriterTest() throws Exception{
//1.创建一个solrj连接solrCloud的服务对象
// 如果想要使用 node01 : 2181 这种方式, 必须保证, node01 是在本地Windows环境下的hosts文件中配置上
// 验证是否配置: 打开cmd 执行: ping node01
String zkHost = "node01:2181,node02:2181,node03:2181";
CloudSolrServer solrServer = new CloudSolrServer(zkHost);

//2. 设置相关的参数: 一个必须, 两个可选
solrServer.setDefaultCollection("collection2"); // 默认连接那个索引库
// zookeeper会帮助我们分配一个(不是很繁忙的节点)solrCloud的节点供我们使用,
solrServer.setZkConnectTimeout(5000); // 从zookeeper中获取连接的超时时间
solrServer.setZkClientTimeout(5000); // 连接zookeeper的超时时间
//3. 从zookeeper中获取连接
solrServer.connect();

//4. 执行添加数据的操作:
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id",1);
doc.addField("title","世界上最傻的焊接工");
doc.addField("content","其实还有比你更傻的, 把自己缝在被罩了");
solrServer.add(doc);

//5. 提交数据
solrServer.commit();


}
  • 删除索引
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 删除索引
@Test
public void delIndexTest() throws Exception{
//1. 创建一个solrj连接solrCloud的服务对象
String zkHost = "192.168.72.141:2181,192.168.72.142:2181,192.168.72.143:2181";
CloudSolrServer solrServer = new CloudSolrServer(zkHost);

//1.1 设置相关的参数: 1个必须 两个可选
solrServer.setDefaultCollection("collection2");

solrServer.setZkClientTimeout(5000);
solrServer.setZkConnectTimeout(5000);

//1.2 获取连接
solrServer.connect();

//2. 添加删除的条件
solrServer.deleteById("1");
// solrServer.deleteByQuery("id:1");

//3. 提交执行
solrServer.commit();
}
  • 查询索引
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 查询索引
@Test
public void indexSearcherTest() throws Exception{
//1. 创建一个solrj连接solrCloud的服务对象
String zkHost = "node01:2181,node02:2181,node03:2181";
CloudSolrServer solrServer = new CloudSolrServer(zkHost);

//1.1 设置相关的参数: 1个必须 两个可选
solrServer.setDefaultCollection("collection2");

//1.2 获取连接
solrServer.connect();

//2. 封装查询的条件
SolrQuery solrQuery = new SolrQuery("*:*");
//3. 执行查询
QueryResponse response = solrServer.query(solrQuery);
//4. 获取数据
SolrDocumentList documentList = response.getResults();
for (SolrDocument document : documentList) {

String id = (String) document.get("id");
String title = (String) document.get("title");
String content = (String) document.get("content");

System.out.println(id+" "+ title +" "+ content);
}

}

5. 搜索门户与solr服务的架构

1541863320833

如何实现系统通信:

​ 1、Web-service:效率不高基于soap协议。项目中不推荐使用。 (http + xml)

​ 2、使用restful形式的服务:http+json。很多项目中应用。如果服务太多,服务之间调用关系混乱,需要治疗服务。

​ 3、使用dubbo。使用rpc协议进行远程调用,直接使用socket通信。传输效率高,并且可以统计出系统之间的调用关系、调用次数。分布式项目中经常采用此种方式

5.dubbox框架

​ Dubbox 是一个分布式服务框架,其前身是阿里巴巴开源项目Dubbo ,被国内电商及互联网项目中使用,后期阿里巴巴停止了该项目的维护,当当网便在Dubbo基础上进行优化,并继续维护,为了与原有的Dubbo区分,故将其命名为Dubbox。 2016年阿里重新将dubbo进行了维护, 并在2017将dubbo贡献给apache, 目前已经成为apache的顶级的项目

​ Dubbox 致力于提供高性能和透明化的RPC远程服务调用方案

1541863595545

1
2
3
4
5
6
7
8
9
10
11
12
13
节点角色说明:
Provider: 暴露服务的服务提供方。
Consumer: 调用远程服务的服务消费方。
Registry: 服务注册与发现的注册中心。
Monitor: 统计服务的调用次调和调用时间的监控中心。
Container: 服务运行容器。
调用关系说明:
0. 服务容器负责启动,加载,运行服务提供者。
1. 服务提供者在启动时,向注册中心注册自己提供的服务。
2. 服务消费者在启动时,向注册中心订阅自己所需的服务。
3. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
4. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

5.1 dubbox的基本使用

​ dubbox官方推荐使用zookeeper作为dubbox的注册中心, 负责服务地址的注册与查找,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小。

需注意, dubbox并没有在maven的中央仓库中存在, 需要手动将Dubbox的jar包安装到我的本地仓库中。

先将dubbo-2.8.4.jar包放到e:\setup, 然后输入命令

1
2
1) 查看maven的环境变量是否存在:  打开cmd  执行 mvn -version
2) 执行下列的命令即可
1
mvn install:install-file -Dfile=e:\setup\dubbo-2.8.4.jar -DgroupId=com.alibaba -DartifactId=dubbo -Dversion=2.8.4 -Dpackaging=jar

1546412674865

  • 1) 创建一个父工程: gossip-dubboxDemo-parent
    • 在父工程中添加如下依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
<!-- 集中定义依赖版本号 -->
<properties>
<junit.version>4.12</junit.version>
<spring.version>4.2.4.RELEASE</spring.version>
<servlet-api.version>2.5</servlet-api.version>
<dubbo.version>2.8.4</dubbo.version>
<zookeeper.version>3.4.7</zookeeper.version>
<zkclient.version>0.1</zkclient.version>
<mybatis.version>3.2.8</mybatis.version>
<mybatis.spring.version>1.2.2</mybatis.spring.version>
<mysql.version>5.1.32</mysql.version>
<druid.version>1.0.9</druid.version>
<freemarker.version>2.3.23</freemarker.version>
<solrj.version>4.10.2</solrj.version>
<javassist.version>3.11.0.GA</javassist.version>
</properties>

<dependencyManagement>
<dependencies>
<!-- spring 相关的依赖包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- mybatis 相关的依赖包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis.spring.version}</version>
</dependency>
<!-- springmvc的相关依赖包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- 数据库的相关依赖包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>

<!-- 日志相关的依赖包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

<!--json相关的依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.28</version>
</dependency>


<!--solr相关依赖-->
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>${solrj.version}</version>
</dependency>


<!--测试相关-->

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>


<!--dubbox相关依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!--zookeeper相关依赖-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>${zkclient.version}</version>
</dependency>

<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>${javassist.version}</version>
</dependency>
</dependencies>

</dependencyManagement>


<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>

</plugins>
</build>
  • 2) 创建服务提供者: dubboxdemo-service (war工程)

    • 2.1) 添加如下内容依赖:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    <dependencies>
    <!-- Spring -->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jms</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    </dependency>
    <!-- dubbo相关 -->
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    </dependency>
    <dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    </dependency>
    <dependency>
    <groupId>com.github.sgroschupf</groupId>
    <artifactId>zkclient</artifactId>
    </dependency>
    <dependency>
    <groupId>javassist</groupId>
    <artifactId>javassist</artifactId>
    </dependency>
    </dependencies>
    <build>
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
    <source>1.8</source>
    <target>1.8</target>
    </configuration>
    </plugin>
    <plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <configuration>
    <!-- 指定端口 -->
    <port>8081</port>
    <!-- 请求路径 -->
    <path>/</path>
    </configuration>
    </plugin>
    </plugins>
    </build>
    • 2.2) 在工程的webapps下创建WEB-INF文件夹,找到web.xml,添加如下内容
    1
    2
    3
    4
    5
    6
    7
    8
    <!-- 加载spring容器 -->
    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext*.xml</param-value>
    </context-param>
    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    • 2.3) 创建一个业务的接口
      • 创建包:com.cyannote.dubboxdemo.service,用于存放业务接口
    1
    2
    3
    4
    5
    6
    7
    package cn.itcast.dubboxdemo.service;
    /**
    * 业务接口
    */
    public interface UserService {
    public String getName();
    }
    • 2.4) 创建业务实现类
      • 创建包com.cyannote.dubboxdemo.service.impl ,用于存放业务实现类。创建业务实现类:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package cn.itcast.dubboxdemo.service.impl;
    import com.alibaba.dubbo.config.annotation.Service;
    import cn.itcast.dubbodemo.service.UserService;
    //注意:Service注解与原来不同,需要引入com.alibaba包下的
    @Service
    public class UserServiceImpl implements UserService {
    public String getName() {
    return "itcast";
    }
    }
    • 2.5) 编写配置文件
      • 在resources下创建applicationContext-service.xml ,内容如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <?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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <dubbo:application name="dubboxdemo-service"/>
    <dubbo:registry protocol="zookeeper" address="192.168.72.141:2181,192.168.72.142:2181,192.168.72.143:2181"/>
    <!--注意:dubbo:annotation用于扫描@Service注解。-->
    <dubbo:annotation package="com.cyannote.dubboxdemo.service" />
    </beans>
    • 2.6) 测试运行
    1
    tomcat7:run
  • 3.服务消费者开发

    • 3.1) 创建Maven工程(WAR)dubboxdemo-web ,在pom.xml引入依赖 ,同“dubboxdemo-service”工程。区别就是把tomcat插件的运行端口改为8082 。
    • 3.2) 在webapps下WEB-INF 目录,找到web.xml,添加如下内容
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <!-- 解决post乱码 -->
    <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>
    <init-param>
    <param-name>forceEncoding</param-name>
    <param-value>true</param-value>
    </init-param>
    </filter>
    <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载-->
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext-web.xml</param-value>
    </init-param>
    </servlet>
    <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    • 3.3) 拷贝业务接口
      • 将“dubboxdemo-service”工程的cn.itcast.dubboxdemo.service 包以及下面的接口拷贝至此工程。
    • 3.4) 编写Controller : 包名: cn.itcast.dubboxdemo.controller
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package cn.itcast.dubboxdemo.controller;
    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 cn.itcast.dubbodemo.service.UserService;
    @Controller
    @RequestMapping("/user")
    public class UserController {
    @Reference
    private UserService userService;
    @RequestMapping("/showName")
    @ResponseBody
    public String showName(){
    return userService.getName();
    }
    }
    • 3.5) 编写spring配置文件
      • 在src/main/resources下创建applicationContext-web.xml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <?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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <mvc:annotation-driven >
    <mvc:message-converters register-defaults="false">
    <bean class="org.springframework.http.converter.StringHttpMessageConverter">
    <constructor-arg value="UTF-8" />
    </bean>
    </mvc:message-converters>
    </mvc:annotation-driven>
    <!-- 引用dubbo 服务 -->
    <dubbo:application name="dubboxdemo-web" />
    <dubbo:registry protocol="zookeeper" address="192.168.72.141:2181,192.168.72.142:2181,192.168.72.143:2181"/>
    <dubbo:annotation package="com.cyannote.dubboxdemo.controller" />
    </beans>
    • 3.6) 测试运行
    1
    2
    3
    tomcat7:run

    在浏览器输入http://localhost:8082/user/showName.do,查看浏览器输出结果

1558167764563

使用步骤:

1) 导包: spring +dubbox+zookeeper

2) 添加spring的配置文件:

1
2
3
4
<dubbo:application name="dubboxdemo-web" />
<dubbo:registry protocol="zookeeper" address="192.168.72.141:2181,192.168.72.142:2181,192.168.72.143:2181"/>
<!--只需要更改扫描包的位置-->
<dubbo:annotation package="com.cyannote.dubboxdemo.controller" />

3) 在服务端添加 @service注解, 在web端添加@Reference

总结:

  • solrCloud :
    • 1) 为什么要使用solrCloud: 搜索高并发 , 索引量大
    • 2) 如果部署solrCloud需要先部署一个zookeeper
      • zookeeper: 是一个集群的管理工具, 在solrCloud中主要用来管理配置文件, 集群状态, solrCloud主节点选举
    • 3) 能够将zookeeper集群 和 solrCloud的集群搭建成功
    • 4) 使用solrCloud的管理命令创建一个新的索引库(符合结构规定)
    • 5) 使用solrj完成对solrCloud的操作
  • dubbox:
    • 基本使用
    • 理解: 为什么要使用dubbox

xml笔记

XML

内容:

​ 1:了解XML语法 以及能够读懂常规定义的xml文件

​ 2:要学习如何对XML文档进行解析


##

1.XML概述

1.1 XML简介

XML: eXtensible Markup Language:可扩展标记型语言
标记型语言:html是标记型语言, 也是使用标签来操作
可扩展:html里面的标签是固定,每个标签都有特定的含义

而XML标签可以自己定义,可以写中文的标签 </person、<猫></猫>

  • xml用途

    html是用于显示数据,xml也可以显示数据(不是主要功能)
    xml主要功能,为了存储数据

  • xml是w3c组织发布的技术

  • xml的应用

    1)不同的系统之间传输数据

​ 2) 用来表示生活中有关系的数据

​ 3 )经常用在文件配置

​ 比如现在连接数据库 肯定知道数据库的用户名和密码,数据名称

​ 如果修改数据库的信息,不需要修改源代码,只要修改配置文件就可以了

1.2 XML的语法

  • Xml文件的组成部分

    1. 文档声明

      ​ xml表示标签的名字

      ​ version表示当前文件的版本号

      ​ encoding表示当前编码, 需要跟文件的编码产生对应关系

​    ps: standalone表示标记此文档是否独立
  1. 元素

    xml中的元素其实就是一个个的标签
    标签分为两种

      a: 包含标签体
        理解: 尖括号全部成对儿出现, 所有的数据都用一对儿尖括号存储
    b: 不包含标签体
        理解: 只有最外层的一个尖括号,括号用/标识结束, 内部的数据都用属性来编写

    注意事项:

    严格区分大小写;


    不能以字母或下划线开头;abc _abc

    不能以xml(或XML、Xml等)开头—-W3C保留日后使用;

    名称字符之间不能有空格或制表符;

    名称字符之间不能使用冒号 : (有特殊用途)

  2. 元素的属性

    一个元素可以有多个属性,每个属性都有它自己的名称和取值。
    属性值一定要用引号(单引号或双引号)引起来。
    属性名称的命名规范与元素的命名规范相同
    元素中的属性是不允许重复的

  3. 注释

    格式:

    ​ <!—被注释的内容 – >

    注意: 注释不能嵌套定义

  4. CDATA区

    为什么要使用CDATA区域?

    ​ 如果我们在标签中写入的内容, 想要带有标签的标记符号的话, 就需要对这段内容进行转义

  1. 特殊字符

    1559027717338

    注意:

    ​ 这种转移可以达到效果, 但是如果操作的数据过多, 编写起来会非常痛苦, 所以, 可以使用CDATA区来解决此问题

  2. 处理指令(PI:Processing Instruction):了解

2.XML约束

为什么要有约束?

XML都是用户自定义的标签,若出现小小的错误,软件程序将不能正确地获取文件中的内容而报错。(如:Tomcat)
XML技术中,可以编写一个文档来约束一个XML的书写规范,这个文档称之为约束

2.1 DTD约束

  • 如何使用DTD约束文件?

    1) 编写DTD文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?xml version="1.0" encoding="UTF-8"?>

    <!ELEMENT 书架(书+)>

    ​ <!ELEMENT 书 (书名,作者,售价)>

    ​ <!ELEMENT 书名 (#PCDATA)>

    ​ <!ELEMENT 作者 (#PCDATA)>

    ​ <!ELEMENT 售价 (#PCDATA)>

    2) 在xml文件中引入DTD文件

    1
    <!DOCTYPE 书架 SYSTEM "book.dtd">

    引入了写好的DTD文件后, 格式就必须跟DTD文件保持一致

  • DTD的细节

    1)语法细节

    在DTD文档中使用ELEMENT关键字来声明一个XML元素。

    • 语法:

    使用规则:

    • (#PCDATA):指示元素的主体内容只能是普通的文本.(Parsed Character Data)

    • EMPTY:用于指示元素的主体为空。比如

    • ANY:用于指示元素的主体内容为任意类型。

    • (子元素):指示元素中包含的子元素

    • 定义子元素及描述它们的关系:

    – 如果子元素用逗号分开,说明必须按照声明顺序去编写XML文档。

    • 如: <!ELEMENT FILE (TITLE,AUTHOR,EMAIL)

    – 如果子元素用”|”分开,说明任选其一。

    • 如:<!ELEMENT FILE (TITLE|AUTHOR|EMAIL)

    – 用+、*、?来表示元素出现的次数

    • 如果元素后面没有+*?:表示必须且只能出现一次

    • +:表示至少出现一次,一次或多次

    • *:表示可有可无,零次、一次或多次

    • ?:表示可以有也可以无,有的话只能有一次。零次或一次

2)定义属性

• 在DTD文档中使用ATTLIST关键字来为一个元素声明属性。

• 语法:

​ <!ATTLIST 元素名

​ 属性名1 属性值类型 设置说明

​ 属性名2 属性值类型 设置说明

​ …

​ >

• 属性值类型:

– CDATA:表示属性的取值为普通的文本字符串

– ENUMERATED (DTD没有此关键字):表示枚举,只能从枚举列表中任选其一,如(鸡肉|牛肉|猪肉|鱼肉)

– ID:表示属性的取值不能重复

• 设置说明

– #REQUIRED:表示该属性必须出现

– #IMPLIED:表示该属性可有可无

– #FIXED:表示属性的取值为一个固定值。语法:#FIXED “固定值”

直接值:表示属性的取值为该默认值

2.2 Schema约束

Schema约束自身就是一个XML文件,但它的扩展名通常为.xsd

一个XML Schema文档通常称之为模式文档(约束文档),遵循这个文档书写的xml文件称之为实例文档。\

XML Schema对名称空间支持得非常好

理解:

​ 名称空间: 相当于package

​ 约束文档: 编写好的Person类

​ 实例文档: 通过Person类创建对象

使用Schema约束文件

​ <约束文件根标签名 xmlns=”绑定名称空间地址”

​ xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance"//名称空间实例

​ xsi:schemaLocation=”绑定名称空间地址 约束文件路径”>


3. XML解析

3.1 XML解析的两种方式

DOM: 将整棵树一口气全部加载到内存当中, 我们可以非常方便的操作任意的标签和属性.
但是, 如果整棵树特别大的时候, 会出现内存溢出的问题

节点: 标签、属性、文本、甚至是换行都称之为节点

SAX: 一个节点一个节点的进行解析

3.2 DOM4J的方法概述

1
2
3
4
5
6
7
8
//获取Document对象
public static Document getDocument() throws Exception {
//创建SAX解析对象
SAXReader reader = new SAXReader();
//解析XML文件,获得Document对象
Document document = reader.read(new FileReader("src/com/itheima/city.xml"));//传入xml文件路径
return document;
}
1
2
3
4
5
6
7
8
9
10
11
12
Dom4J的常用方法:
Document
Element getRootElement() :获取根元素对象(根标签)
Element
List elements() :获取所有的子元素
List elements(String name):根据指定的元素名称来获取相应的所有的子元素
Element element(String name):根据指定的元素名称来获取子元素对象,如果元素名称重复,则获取第一个元素
String elementText(String name) :根据指定的子元素名称,来获取子元素中的文本
String getText() :获取当前元素对象的文本
void setText(String text):设置当前元素对象的文本
String attributeValue(String name):根据指定的属性名称获取其对应的值
public Element addAttribute(String name,String value):根据指定的属性名称和值进行添加或者修改BeanUtils的常用方法
1
2
3
4
5
6
//将修改后的数据戏写入xml文件
public static void write2XML(Document document) throws Exception,FileNotFoundException {
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter(new FileOutputStream("src/com/itheima/city.xml"), format);
writer.write(document);
}

SpringMVC&SSM整合

SpringMVC

1.SpringMVC的基本概念

1.1 关于三层架构和mvc

三层架构

在B/S架构中,系统标准的三层架构包括:表现层、业务层、持久层。

表现层:
也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web层,web需要接收http请求,完成http响应。
表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。
表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。
表现层的设计一般都使用MVC模型。(MVC是表现层的设计模型,和其他层没有关系)
业务层:
也就是我们常说的service层。它负责业务逻辑处理,和我们开发项目的需求息息相关。web层依赖业务层,但是业务层不依赖web层。
业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务一致性。(也就是我们说的,事务应该放到业务层来控制)
持久层:
也就是我们是常说的dao层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。通俗的讲,持久层就是和数据库交互,对数据库表进行增删改查的。

img

mvc模型

mvc全名是Model view Controller,是模型(model)-视图(view)-控制器(Controller)的缩写,是一种用于设计创建Web应用程序表现层的模式.MVC中每个部分各司其职:

Model(模型):
通常指的就是我们的数据模型。作用一般情况下用于封装数据。
View(视图):
通常指的就是我们的jsp或者html。作用一般就是展示数据的。
通常视图是依据模型数据创建的。
Controller(控制器):
是应用程序中处理用户交互的部分。作用一般就是处理程序逻辑的。

1.2 SpringMVC概述

SpringMVC是什么?

SpringMVC是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于 Spring FrameWork 的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用 Spring 的 Spring MVC 框架或集成其他MVC开发框架,如Struts1,Xwork、Struts2等。

它通过一套注解,让一个简单的Java,而无须实现任何接口。同时它还支持编程风格的请求。

SpringMVC在三层架构中的位置

img

SpringMVC的优势

1、清晰的角色划分:

​ 前端控制器(DispatcherServlet)

​ 请求到处理器映射器(HandlerMapping)

​ 处理器适配器(HandlerAdapter)

​ 视图解析器(ViewResolver)

​ 处理器或页面控制器(Controller)

​ 验证器( Validator)

​ 命令对象(Command 请求参数绑定到的对象就叫命令对象)

​ 表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象)。

2、分工明确,而且扩展点相当灵活,可以很容易扩展,虽然几乎不需要。

3、由于命令对象就是一个POJO,无需继承框架特定API,可以使用命令对象直接作为业务对象。

4、和Spring 其他框架无缝集成,是其它Web框架所不具备的。

5、可适配,通过HandlerAdapter可以支持任意的类作为处理器。

6、可定制性,HandlerMapping、ViewResolver等能够非常简单的定制。

7、功能强大的数据验证、格式化、绑定机制。

8、利用Spring提供的Mock对象能够非常简单的进行Web层单元测试。

9、本地化、主题的解析的支持,使我们更容易进行国际化和主题的切换。

10、强大的JSP标签库,使JSP编写更容易。

………………还有比如风格的支持、简单的文件上传、约定大于配置的契约式编程支持、基于注解的零配置支持等等

SpringMVC和Struts2的优劣分析

共同点:

它们都是表现层框架,都是基于MVC模型编写的。

它们的底层都离不开原始ServletAPI(HttpServletRequest、HttpServletResponse…)。

它们处理请求的机制都是一个核心控制器。

区别:

Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter

Spring MVC 是基于方法设计的,而Struts2是基于类,Struts2每次执行都会创建一个动作类。所以Spring MVC 会稍微比 Struts2 快些。

Spring MVC 使用更加简洁,同时还支持 JSR303, 处理 ajax 的请求更方便 ,而struts2处理ajax的时候,需要导入一个struts2的插件包,并通过返回类型指定ajax的json数据。

(JSR303 是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面,就可以在需要校验的时候进行校验了。) (回去之后,有兴趣可以搜一下springMVC校验机制)

Struts2 的表达式使页面的开发效率相比更高些,但执行效率并没有比提升,尤其是的表单标签,远没有执行效率高。

2.SpringMVC的执行流程

img

3.请求参数绑定和常用注解

3.1 请求参数绑定

(1)绑定机制

​ 【1】表单提交的数据都是k=v格式的 username=haha&password=123

​ 【2】SpringMVC的参数绑定过程是把表单提交的请求参数,作为控制器中方法的参数进行绑定的

​ 【3】要求:提交表单的name和参数的名称是相同的

(2)支持的数据类型

​ 【1】基本数据类型和字符串类型

​ (1)提交表单的name和参数的名称是相同的

​ (2)区分大小写

​ 【2】实体类型(JavaBean)

​ (1)提交表单的name和JavaBean中的属性名称需要一致

​ (2)如果一个JavaBean类中包含其他的引用类型,那么表单的name属性需要编写成:对象.属性 例如:address.name

​ 【3】集合数据类型(List、map集合等)

​ (1)JSP页面编写方式:list[0].属性

​ (2)JSP页面编写方式:map[‘one’].属性

3.2 自定义类型转换器

(1)表单提交的任何数据类型全部都是字符串类型,但是后台定义Integer类型,数据也可以封装上,说明SpringMVC框架内部会默认进行数据类型转换。

(2)如果想自定义数据类型转换,可以实现Converter的接口

默认支持的日期格式是:2010/11/12,如果输入2010-11-12呢?则会报错,怎么办呢?

【1】自定义类型转换器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import org.springframework.core.convert.converter.Converter;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
* 把字符串转换成日期的转换器
*/
public class StringToDateConverter implements Converter<String, Date> {

/**
* 进行类型转换的方法
*/
public Date convert(String source) {
// 判断
if(source == null) {
throw new RuntimeException("参数不能为空");
}
try {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
// 解析字符串
Date date = df.parse(source);
return date;
} catch (Exception e) {
throw new RuntimeException("类型转换错误");
}
}
}

【2】注册自定义类型转换器,在springmvc.xml配置文件中编写配置

1
2
3
4
5
6
7
8
9
10
11
<!-- 注册自定义类型转换器 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.cyannote.utils.StringToDateConverter"></bean>
</set>
</property>
</bean>

<!-- 配置spring开启注解mvc的支持 -->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

3.3 常用注解

@RequestParam注解

作用:

把请求中指定名称的参数给控制器中的形参赋值。

属性:

value:请求参数中的名称。

required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错。

defaultValue:表示默认值,如果不传递值

@RequestBody注解

作用:
1:@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的)
2:用于获取请求体内容。直接使用得到是key=value&key=value…结构的数据。 get请求方式不适用。

属性:
required:是否必须有请求体。默认值是:true。
当取值为true时,get请求方式会报错。
如果取值为false,get请求得到是null。

@ResponseBody响应json数据

使用@RequestBody获取请求体数据

response.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
$(function(){
$("#btn").click(function(){
// alert("ok");
$.ajax({
url:"user/testJson",
contentType:"application/json;charset=UTF-8",
data:'{"username":"tom","password":"123","age":30}',
dataType:"json",
type:"post",
success:function(data){
alert(data);
alert(data.addressName);
}
});
})
})
</script>

在springmvc.xml中使用:

1
2
<!--去掉所有的静态资源,让DispatchServlet不能拦截静态资源-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>

使用@ResponseBody注解将JavaBean对象转换成Json返回

UserController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 获取请求体的数据,响应数据Json
* @param body
*/
@RequestMapping("/testJson")
public @ResponseBody User testJson(@RequestBody User user) {
// 请求user
System.out.println(user);
// 响应u
User u = new User();
u.setUsername("张三");
u.setPassword("123");
u.setAge(18);
return u;
}

@PathVariable注解

使用说明
作用:
用于绑定url中的占位符。例如:请求url中 /delete/{id}/{name},这个{id},{name}就是url占位符。
传统方式:/delete?id=3&name=zhangsan
rest风格:/delete/3/zhangsan
url支持占位符是spring3.0之后加入的。是springmvc支持restful风格URL的一个重要标志。
属性:
value:用于指定url中占位符名称。
required:是否必须提供占位符。

@RequestHeader注解

作用:

用于获取请求消息头。

属性:

value:提供消息头名称

required:是否必须有此消息头

注:

在实际开发中一般不怎么用。

@CookieValue 注解

作用:

用于把指定cookie名称的值传入控制器方法参数。

属性:

value:指定cookie的名称。

required:是否必须有此。

@ModelAttribute注解

作用:

该注解是SpringMVC4.3版本以后新加入的。它可以用于修饰方法和参数。

出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。

出现在参数上,获取指定的数据给参数赋值。

属性:

value:用于获取数据的key。key可以是POJO的属性名称,也可以是map结构的key。

应用场景:

当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据。

基于POJO属性的基本使用

anno.jsp

1
2
3
4
5
6
7
8
<h3>测试@ModelAttribute注解</h3>
需求: 修改用户信息,要求用户的日期不能修改 jsp的代码:
<!-- 修改用户信息 -->
<form action="anno/testModelAttribute" method="post">
用户名称:<input type="text" name="username" ><br/>
用户年龄:<input type="text" name="age" ><br/>
<input type="submit" value="保存">
</form>

AnnoController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
// ModelAttribute的用法
@ModelAttribute // 在执行的方法之前运行
public void showModel(User user) {
System.out.println("执行了showModel方法"+user);
user.setBirthday(new Date());
}

/** * 模拟修改用户方法 * @param user * @return */
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user) {
System.out.println("控制器中处理请求的方法:修改用户:"+user);
return "success";
}

基于Map的应用场景:ModelAttribute修饰方法不带返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@ModelAttribute // 在执行的方法之前运行
public void showModel(String username, Map<String,User> maps) {
//模拟去数据库查询
User user = findUserByName(username);
maps.put("abc",user);
System.out.println("执行了showModel方法"+user);
}

/** * 模拟修改用户方法 * @param user */
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute(value = "abc") User user) {
System.out.println("控制器中处理请求的方法:修改用户:"+user);
return "success";
}

/** * 模拟去数据库查询 * @param username * @return User*/
private User findUserByName(String username) {
User user = new User();
user.setUsername(username);
user.setAge(19);
user.setBirthday(new Date());
return user;
}

@SessionAttributes注解

作用:

用于多次执行控制器方法间的参数共享。 放置到类的上面。

属性:

value:用于指定存入的属性名称

type:用于指定存入的数据类型。

相当于将数据存放到Session中。

3.4 REST风格URL

REST(英文:Representational State Transfer,简称REST,说的意思是:表现层状态转变,或者叫做 “表述性状态转移”)描述了一个架构样式的网络系统,REST相比于SOAP(Simple Object Access protocol,简单对象访问协议)以及XML-RPC更加简单明了,无论是对URL的处理还是对Payload的编码,REST都倾向于用更加简单轻量的方法设计和实现。值得注意的是REST并没有一个明确的标准,而更像是一种设计的风格。

它本身并没有什么实用性,其核心价值在于如何设计出符合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 用来删除资源。

传统的实例:唯一的URL地址(统一资源定位符),找到对应的资源

1562658993659

restful的示例: URI(统一资源标识符)

1562659031196

基于HiddentHttpMethodFilter的示例(REST风格代码)

作用:
由于浏览器 form 表单只支持 GET 与 POST 请求,而DELETE、PUT 等 method 并不支持,Spring3.0 添加了一个过滤器,可以将浏览器请求改为指定的请求方式,发送给我们的控制器方法,使得支持 GET、POST、PUT 与DELETE 请求。
使用方法:
第一步:在web.xml中配置该过滤器。

1
2
3
4
5
6
7
8
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

第二步:请求方式必须使用post请求。

第三步:按照要求提供_method请求参数,该参数的取值就是我们需要的请求方式。

源码分析:

img

案例:

anno.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!--resfful风格:jsp中示例代码:-->
<!-- 保存 -->
<form action="anno/testPathVariable" method="post">
用户名称:<input type="text" name="username"><br/>
<!-- <input type="hidden" name="_method" value="POST"> -->
<input type="submit" value="保存"></form>
<hr/>
<!-- 更新 -->
<form action="anno/testPathVariable" method="post">
用户名称:<input type="text" name="username"><br/>
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="更新">
</form>
<hr/>
<!-- 删除 -->
<form action="anno/testPathVariable/1" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="删除">
</form>
<hr/>
<!-- 查询一个 -->
<form action="anno/testPathVariable/1" method="post">
<input type="hidden" name="_method" value="GET">
<input type="submit" value="查询">
</form>
<hr/>

AnnoController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 请求参数
@RequestMapping(path = "/testPathVariable",method = RequestMethod.POST)
public String save(User user){
System.out.println("Hello SpringMVC!!,测试@PathVariable,新增-请求方式Post");
System.out.println(user);
return "success";// 响应结果
}
// 请求参数
@RequestMapping(path = "/testPathVariable",method = RequestMethod.PUT)
public String update(User user){
System.out.println("Hello SpringMVC!!,测试@PathVariable,更新-请求方式Put");
System.out.println(user);
return "success";// 响应结果
}
// 请求参数
@RequestMapping(path = "/testPathVariable/{uid}",method = RequestMethod.DELETE)
public String delete(@PathVariable(value = "uid") Integer id){
System.out.println("Hello SpringMVC!!,测试@PathVariable,删除-请求方式Delete");
System.out.println(id);
return "success";// 响应结果
}
// 请求参数
@RequestMapping(path = "/testPathVariable/{uid}",method = RequestMethod.GET)
public String findById(@PathVariable(value = "uid") Integer id){
System.out.println("Hello SpringMVC!!,测试@PathVariable,查询一个-请求方式Get");
System.out.println(id);
return "success";// 响应结果

测试结果:

img

4.SpringMVC实现文件上传

4.1文件上传的三要素

A form表单的enctype取值必须是:multipart/form-data (支持二进制数据)

(默认值是:application/x-www-form-urlencoded) (支持传递参数,例如:?username=zhangsan&age=18)

enctype:是表单请求正文的类型

B method属性取值必须是Post

C 提供一个文件选择域<input type="file" />

4.2 SpringMVC传统方式文件上传

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<!--核心控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--post请求中文乱码过滤-->
<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>

</web-app>

springmvc.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--配置静态资源不过滤-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<!--开启扫描-->
<context:component-scan base-package="com.cyannote"></context:component-scan>
<!--配置文件解析器对象,要求id名称必须为multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
</beans>

UploadController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.cyannote.Controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.UUID;

/****
* author:tan
* data:2019-07-09 16:54
* description:
**/
@Controller
@RequestMapping(path = "/user")
public class UploadController {
@RequestMapping(path = "/upload")
//MultipartFile对象名应与file的name值一致
public String upload(HttpServletRequest request, MultipartFile upload) throws IOException {
System.out.println("springMVC的上传文件...");
//获取到项目根目录的upload文件夹绝对路径
String path = request.getSession().getServletContext().getRealPath("/upload");
//创建file对象
File file = new File(path);
//判断是否存在
if(!file.exists()){
file.mkdirs();
}
//获取文件名
String filename = upload.getOriginalFilename();
//生成唯一标识
String uuid = UUID.randomUUID().toString().replace("-","").toUpperCase();
filename = uuid + "_" + filename;
//使用upload对象中方法就直接上传文件
upload.transferTo(new File(file,filename));
return "succuss";
}
}

index.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>文件上传</h1>
<form method="post" action="user/upload" enctype="multipart/form-data">
<input type="file" name="upload"><br>
<input type="submit" value="提交">
</form>
</body>
</html>

success.jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>上传成功!</h3>
</body>
</html>

4.3 springmvc跨服务器方式的文件上传

分服务器的目的:

在实际开发中,我们会有很多处理不同功能的服务器。例如:

应用服务器:负责部署我们的应用 (源码)

数据库服务器:运行我们的数据库

缓存和消息服务器:负责处理大并发访问的缓存和消息

文件服务器:负责存储用户上传文件的服务器。

图片服务器:负责存储用户上传的图片的信息。

(注意:此处说的不是服务器集群)

分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。

第一步创建工程
1562663904901

1562664098090

pom.xml添加依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.18.1</version>
</dependency>

UploadController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//跨服务器上传
@RequestMapping(path = "/upload2")
//MultipartFile对象名应与file的name值一致
public String upload(MultipartFile upload) throws IOException {
System.out.println("跨服务器上传文件...");
//获取到项目根目录的upload文件夹绝对路径
String path = "http://localhost:9090/upload/";

//获取文件名
String filename = upload.getOriginalFilename();
//生成唯一标识
String uuid = UUID.randomUUID().toString().replace("-","").toUpperCase();
filename = uuid + "_" + filename;
//上传文件
//创建客户端对象
Client client = Client.create();
filename = path+filename;
//连接图片服务器
WebResource webResource = client.resource(filename);
//上传文件
webResource.put(upload.getBytes());
return "succuss";
}

注意:如果抛出异常:returned a response status of 403 Forbidden

需要在这个存储图片的项目所在的tomcat中配置可写操作。具体的是在Tomcat目录下的conf文件夹下的web.xml中加入

1
2
3
4
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>

4.4 SpringMVC的异常处理

第一步:编写自定义异常类

创建包com.cyannote.exception,创建类SysException.java。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 自定义异常类
*/
public class SysException extends Exception {

// 异常提示信息
private String message;

public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public SysException(String message) {
this.message = message;
}
}

第二步:自定义异常处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 编写异常处理器
*/
public class SysExceptionResolver implements HandlerExceptionResolver {

/**
* 跳转到具体的错误页面的方法
*/
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, @Nullable Object o, Exception e) {
e.printStackTrace();
SysException se = null;
// 获取到异常对象
if(e instanceof SysException) {
se = (SysException) e;
}else {
se = new SysException("请联系管理员");
}
ModelAndView mv = new ModelAndView();
// 存入错误的提示信息
mv.addObject("message", se.getMessage());
// 跳转的Jsp页面
mv.setViewName("error");// 跳转到WEB-INF/pages/error.jsp
return mv;
}
}

第三步:配置异常处理器(跳转到错误提示页面)

springmvc.xml

1
2
<!-- 配置异常处理器 -->
<bean id="sysExceptionResolver" class="com.itheima.exception.SysExceptionResolver"/>

第四步:修改UserController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
// 自定义异常处理
@RequestMapping(path="/testException")
public String testException() throws SysException{
System.out.println("执行了testException方法!");
try {
int a = 10/0;
} catch (Exception e) {
// 在控制台打印
e.printStackTrace();
throw new SysException("服务器繁忙,稍后再试...");
}
return "success";
}

第五步:指定error.jsp错误页面

1
2
3
4
5
6
7
8
9
10
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>访问失败</h1>
${message}<br>
</body>
</html>

4.5 SpringMVC中的拦截器

Spring MVC 的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器(Controller)进行预处理和后处理。
用户可以自己定义一些拦截器来实现特定的功能(权限控制、日志处理等)。
谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
说到这里,可能大家脑海中有了一个疑问,这不是我们之前学的过滤器吗?是的它和过滤器是有几分相似,但是也有区别,接下来我们就来说说他们的区别:

1
2
3
4
5
6
7
8
9
过滤器和拦截器的区别:
区别1:
过滤器是servlet规范中的一部分,任何java web工程都可以使用。
拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能用。
区别2:
过滤器在url-pattern中配置了/*之后,可以对所有要访问的资源拦截。
拦截器它是只会拦截访问的控制器方法(只会拦截Controller)
核心控制器设置成/,表示拦截所有请求
核心控制器设置成*.do(*.action),表示拦截.do结尾的url请求

它也是AOP思想的具体应用。

我们要想自定义拦截器, 要求必须实现:HandlerInterceptor

img

第一步:自定义拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyInterceptor1 implements HandlerInterceptor{

/**
* controller方法执行前,进行拦截的方法
* return true放行
* return false拦截
* 可以使用转发或者重定向直接跳转到指定的页面。
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("访问Controller类之前执行...");
return true;
}
}

第二步:配置拦截器
在springmvc.xml中配置拦截器

1
2
3
4
5
6
7
8
9
10
11
<!--配置拦截器类-->
<mvc:interceptors>
<mvc:interceptor>
<!-- 哪些方法进行拦截 -->
<mvc:mapping path="/user/*"/>
<!--哪些方法不进行拦截-->
<!--<mvc:exclude-mapping path="/user/save"/>-->
<!-- 注册拦截器对象 -->
<bean class="com.itheima.interceptor.MyInterceptor1"></bean>
</mvc:interceptor>
</mvc:interceptors>

5.SSM整合

5.1搭建整合环境

整合说明:整合说明:SSM整合可以使用多种方式,咱们会选择XML(第三方对象) + 注解(自己new的对象)的方式

img

整合思路:

(1):先搭建整合的环境

(2):先把Spring的配置搭建完成

(3):再使用Spring整合SpringMVC框架

(4):最后使用Spring整合MyBatis框架

5.2 登陆注册案例

1562673826620

第一步:创建实体类 创建User.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class User  implements Serializable{
private String number;
private String name;
private String password;
private String mobile;
private String qq;
private String email;

public String getNumber() {
return number;
}

public void setNumber(String number) {
this.number = number;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getMobile() {
return mobile;
}

public void setMobile(String mobile) {
this.mobile = mobile;
}

public String getQq() {
return qq;
}

public void setQq(String qq) {
this.qq = qq;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

@Override
public String toString() {
return "User{" +
"number='" + number + '\'' +
", name='" + name + '\'' +
", password='" + password + '\'' +
", mobile='" + mobile + '\'' +
", qq='" + qq + '\'' +
", email='" + email + '\'' +
'}';
}
}

第二步:创建接口UserDao.java

1
2
3
4
5
6
7
8
public interface UserDao {
//登陆
@Select(value = "select * from user where number = #{number} and password = #{password}")
User findByNameAndPassword(User user);
//注册
@Insert(value = "insert into user (number,name,password,mobile,qq,email) values (#{number},#{name},#{password},#{mobile},#{qq},#{email})")
int saveUser(User user);
}

第三步:创建接口UserService.java及其实现类UserServiceImpl.java

1
2
3
4
5
6
public interface UserService {
//登陆
User findByNameAndPassword(User user);
//注册
int saveUser(User user);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public User findByNameAndPassword(User user) {
return userDao.findByNameAndPassword(user);
}

@Override
public int saveUser(User user) {
return userDao.saveUser(user);
}
}

第四步:创建UserController.java和IndexController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Controller
@RequestMapping(path = "/user")
public class UserController {
@Autowired
private UserService userService;

@RequestMapping(path = "/login")
public String login(User user, HttpServletRequest request){
User u = userService.findByNameAndPassword(user);
if (u==null){
return "redirect:/login.html";
}else {
request.getSession().setAttribute("user",u);
return "redirect:/index/welcome";
}
}

@RequestMapping(path = "/save")
public String save(User user){
int i = userService.saveUser(user);
if(i>0){
return "redirect:/login.html";
}else {
return "redirect:/register.html";
}
}
}
1
2
3
4
5
6
7
8
@Controller
@RequestMapping(path = "/index")
public class IndexController {
@RequestMapping(path = "/welcome")
public String welcome(){
return "index";
}
}

第五步:创建拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断Session中是否存有用户信息
Object user = request.getSession().getAttribute("user");
if(user == null){
//没有登录
response.sendRedirect(request.getContextPath()+"/login.html");
return false;
}
return true;
}
}

第六步:配置web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!--核心控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--post请求乱码过滤-->
<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>
</web-app>

第七步:配置springmvc.xml和applicationContext.xml

springmvc.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--导入applicationContext-service.xml-->
<import resource="applicationContext-service.xml"></import>
<!--开启包扫描-->
<context:component-scan base-package="com.cyannote"></context:component-scan>
<!--注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--静态资源不过滤-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--指定拦截路径-->
<mvc:mapping path="/**"/>
<!--不拦截-->
<mvc:exclude-mapping path="/*.html"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/user/login"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/user/save"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/css/**"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/img/**"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/plugins/**"></mvc:exclude-mapping>
<bean class="com.cyannote.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
</beans>

applicationContext-dao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///test03"></property>
<property name="user" value="root"></property>
<property name="password" value="abc"></property>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启dao包映射扫描-->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.cyannote.dao"></property>
</bean>
</beans>

applicationContext-service.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--加载applicationContext-dao.xml-->
<import resource="applicationContext-dao.xml"></import>
<!--配置事务管理器-->
<bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--事务注解扫描-->
<tx:annotation-driven transaction-manager="TransactionManager"></tx:annotation-driven>
</beans>

第八步:页面

login.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<form class="sui-form" action="/user/login" method="post">
<div class="input-prepend"><span class="add-on loginname"></span>
<input id="prependedInput" type="text" name="number" placeholder="邮箱/用户名/手机号" class="span2 input-xfat">
</div>
<div class="input-prepend"><span class="add-on loginpwd"></span>
<input id="prependedInput" type="password" name="password" placeholder="请输入密码" class="span2 input-xfat">
</div>
<div class="setting">
<label class="checkbox inline"><input name="m1" type="checkbox" value="2" checked="">自动登录</label>
<span class="forget">忘记密码?</span>
</div>
<div class="logined">
<button class="sui-btn btn-block btn-xlarge btn-danger" type="submit">登&nbsp;&nbsp;录</button>
</div>
</form>

在index.jsp中获取登录信息

1
2
3
4
5
<!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
<span class="sr-only">Toggle navigation</span>
</a>
欢迎你:${sessionScope.user.number}