[SSM架构]springboot笔记

框架基础介绍

框架概念

框架(Framework)是一个半成品软件,将所有的、公共的、重复的功能解决掉,帮助程序快速高效的进行开发,他是可重复的,可拓展的。

常见的框架--SSM

Spring:他是整合其他框架的框架,他的核心是IOC和AOP,他由20多个模块构成,在很多领域都提供了很好的解决方案。

SpringMVC:专门用来优化控制器(servlet),提供了极容易的数据提交,数据携带,页面跳转等功能。

Mybatis:是持久化层的一个框架,用来进行数据库访问的优化,专注于sql语句,极大的简化了JDBC的访问。

项目搭建

新建项目Spring Initializr

 选择启动服务url除了默认通用url之外,可选择阿里云https://start.aliyun.com/

Spring Initializr项目配置

组和工件

组(GroupID)是项目组织的唯一标识符,实际对应JAVA的结构,是main目录里java的目录结构。

工件(ArtifactID)是项目的唯一标识符,实际对应项目的名称,就是项目根目录的名称。

命名规则:

1.GroupID一般分为多个段,第一个段为域,第二个段为公司名。域又分为org、com、cn等,其中org为非盈利组织,com为商业组织。

2.ArtifactID是项目名称。

Jar和War包

Jar、War在文件结构上,二者并没有什么不同,他们都采用zip或者rar档案文件压缩格式。但是他们的使用目的是有区别的:

Jar:

1.Jar文件(拓展名为.Jar,Java Application Archive)包含Java类的普通库、资源(resource)、辅助文件(auxiliary files)等。

2.Jar包是Java打的包,一般只是包括一些编译后的class文件和一些部署文件,在声明了Main_class之后是可以用java命令运行的,例如java -jar xxxx.jar。

3.Jar包通常是开发时要引用通用类,打成包成便于存放管理。

War:

1.War文件(拓展名为.War,Web Application Archive)包含全部Web应用程序。在这种情形下,一个Web应用程序被定义为单独的一组文件、类和资源,用户可以对War文件进行封装,并把他作为小型服务程序(servlet)来访问。

2.War包可以理解为JavaWeb打的包,是一个web模块,包括写的代码编译成的class文件,依赖的包,配置文件,所有的网站页面,包括html,jsp等等。一个war包可以理解为是一个web项目,里面是项目的所有东西。

3.War包需要发布到一个容器里面,那Tomcat来说,将War文件包放置在他的\webapps\目录下面,启动Tomcat,这个包可以自动进行解压,也就是你的web目录,相当于发布了。

4.WAR是Sun提出的一种Web应用程序格式,与JAR类似,也是许多文件的一个压缩包。这个包中的文件会按照一定的目录结构来组织:通常其根目录下包含有Html和JSP文件或者包含这两种文件的目录,另外还有一个WEB-INF目录,这个目录很重要。通常在WEB-INF目录下有一个web.xml文件和一个classes目录,web.xml是这个应用的配置文件,而classes目录下则包含编译好的Servlet类和Jsp或者Servlet所依赖的其他类(JavaBean)。

关于Java版本

推荐学习Java8,因为Java8现在是最稳定的版本,并且支持现有的99%的框架,所以大家一直在使用Java8,对于一个成熟的公司来说,代码只是辅助业务的手段,而非目的。

从Java9开始,Java版本的发布开始让人有些眼花缭乱了,每隔6个月,都会冒出一个新版本出来,Java10,Java11,Java12,Java13,到2020年8月份,Java15正式发布。

这么多的版本中,只有Java8,Java11和未来的Java17是长期支持版本(LTS).

这里指定Java版本为8

 

选择依赖项

在新建项目中添加依赖项:

必定添加开发工具(Spring Boot DevTools)和Web(Spring Web)

其他依赖项可以在此处(新建项目)中添加,也可以在后续生成的pom.xml文件中使用Maven添加

在pom.xml中添加依赖项目方法:

1.在pom.xml文件中找到<dependencies>标签,该标签包裹的就是当前所有依赖相对应的jar包声明

2.在<dependencies>标签中添加想要的依赖,

方法一:可直接输入依赖项的名称,自动弹出提示,回车确定添加,例如:添加单元测试Junit所需依赖,输入junit自动弹出提示,回车确定添加

方法二:可访问https://mvnrepository.com/,在搜索栏中直接搜索

例如:添加mybatis所需依赖

选择第一个搜索结果,选择对应的版本,(以3.4.6版本为例)

 复制内容,粘贴到pom.xml文件中的<dependencies>标签内,点击idea应用界面工作区的右上角的Maven按钮,自动添加依赖

注意:本项目中需要搭建Mybatis开发环境,无论是使用在新建项目中添加依赖的方法,还是在pom.xml中手动添加依赖的方法,都必须添加mybatis和mysql这两个依赖项

 

 

 

项目名称和位置

自定义项目名称和位置

 

点击确定,项目构建成功

一些问题的解决办法

pom.xml文件version报错

 解决办法一:版本号过高,降低版本号(一般会自动匹配jdk生成对应版版本号)

 

 解决办法二:在IDEA中的Maven中配置错误

点击文件->设置

 在构建、执行、部署工具->Maven中更改maven主路径

 

使用Mybatis框架结构搭建项目

MyBatis概述

1.MyBatis是一个优秀的基于Java的持久层框架,内部封装了JDBC,开发者只需要关注sql语句本身,而不需要处理加载驱动、创建链接、创建statement、关闭链接,资源等繁杂过程。

2.MyBatis通过xml或注解两种方式将要执行的各种sql语句配置起来,并通过Java对象和sql的动态参数进行映射生成最终执行的sql语句,最后由mubatis框架执行sql并将结果映射为Java对象并返回。

3.MyBatis解决的主要问题:

1)减轻使用JDBC的复杂性,不用编写、重复创建Connection ,Statement;

2)不用编写关闭资源代码;

3)直接使用Java对象,表示结果数据;

MyBatis框架结构

1、 mybatis配置文件

【SqlMapConfig.xml】:此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。


【mapper.xml】:文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。


【SqlMapConfig.xml】:是mybatis的核心文件。mybatis将dao层与sql语句分离开来,虽然写的时候分离开来了,但是执行的时候还是要依靠sql语句,所以我们的sql语句写在Mapper.xml中。我们在加载核心的时候,会加载他下面的Mapper.xml,所以sql语句便会加载进去了。我们只需要在SqlMapConfig.xml中引入Mapper.xml就可以了,所以最后只需要加载SqlMapConfig.xml这一个核心配置文件。


2、 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂。工厂能帮我们去加载核心配置文件。加载了核心配置文件后就创建session,通过session可以对数据库进行操作。


3、 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。


4、 mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。Executor是执行者,我们不需要管,因为mybatis已经为我们封装好了。mybatis直接执行sql语句。


5、 Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。


6、 Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。


7、 Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。


8、Mapped Statement是输入与输出中间过程中产生的一些对象,通过这些对象去访问数据库。

核心配置文件

 添加mybatis-config.xml核心配置文件

 在src/main/resource下新建添加mybatis-config.xml

 在mybatis-config.xml写入模板代码

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

添加db资源包

在src/main/resource下新建->资源包,指定包名为db

 

 

 在新建的db资源包内写入mysql数据库链接信息

下面的代码有五行(包括注释)每行后面不能有空格,每个字母之间也不能有空格

1
2
3
4
5
#mysql数据库链接信息<br>#com.mysql.jdbc.Driver高版本数据库链接与添加cj:com.mysql.cj.jdbc.Driver
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username=root
password=root

 在mybatis-config.xml文件<configuration>标签中添加链接数据库相关代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--    读取外部的数据源配置信息-->
    <properties resource="db.properties"></properties>
 
    <!--    指定类别名所在的包路径-->
    <typeAliases>
        <package name="com.example.demo"/>
    </typeAliases>
 
    <environments default="demo">
        <environment id="demo">
<!--            使用JDBC的事务管理器-->
            <transactionManager type="JDBC"></transactionManager>
<!--            以连接池方式连接mydql数据库-->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>

 添加SQL映射文件

在src/main/resources下新建一个mapper目录,在该目录下新建GradeMapper.xml文件

在GradeMapper.xml中写入模板代码

1
2
3
4
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

添加<mapper>标签,并继续往<mapper>标签内添加数据库操作相关语句,例如

1
2
3
4
5
<!--com.example.demo.Dept.queryCount-->
<mapper namespace="com.example.demo.Dept"><!-- 名字空间自己随便,一般指定所用的类的位置 -->
    <select id="queryCount" resultType="Long">
        select count(1) from tb_dept
    </select><br></mapper>

 在mybatis-config.xml的dependencies标签下添加mapper文件的映射

1
2
3
4
<!--    mapper映射文件 -->
    <mappers>
        <mapper resource="mapper/GradeMapper.xml"></mapper>
    </mappers>

 添加实体类

新建一个java类用作数据库实体映射的实体类,添加Serializable接口,然后添加属性和get/set以及toString方法,例如:

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 Dept implements Serializable {
    private Integer deptNo;
    private String deptName;
    private String remark;
     
    public Integer getDeptNo() {
        return deptNo;
    }
 
    public void setDeptNo(Integer deptNo) {
        this.deptNo = deptNo;
    }
 
    public String getDeptName() {
        return deptName;
    }
 
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
 
    public String getRemark() {
        return remark;
    }
 
    public void setRemark(String remark) {
        this.remark = remark;
    }
     
    @Override
    public String toString() {
        return "Dept{" +
                "deptNo=" + deptNo +
                ", deptName='" + deptName + '\'' +
                ", remark='" + remark + '\'' +
                '}';
    }
}

 关于Serializable接口:

Serializable是一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才是可序列化的。因此如果要序列化某些类的对象,这些类就必须实现Serializable接口。而实际上,Serializable是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。
序列化的应用场景

1.    比如说你的内存不够用了,那计算机就要将内存里面的一部分对象暂时的保存到硬盘中,等到要用的时候再读入到内存中,硬盘的那部分存储空间就是所谓的虚拟内存。在比如过你要将某个特定的对象保存到文件中,我隔几天在把它拿出来用,那么这时候就要实现Serializable接口。

2.    在进行Java的Socket编程的时候,你有时候可能要传输某一类的对象,那么也就要实现Serializable接口。最常见的你传输一个字符串,它是JDK里面的类,也实现了Serializable接口,这样做为的是将数据变为二进制来传输,所以可以在网络上传输。

3.    如果要通过远程的方法调用(RMI)去调用一个远程对象的方法,如在计算机A中调用另一台计算机B的对象的方法,那么你需要通过JNDI服务获取计算机B目标对象的引用,将对象从B传送到A,就需要实现序列化接口。

备注:
Serializable这个接口其实是个空接口。其实,看一下接口的注释说明就知道,当我们让实体类实现Serializable接口时,其实是在告诉JVM此类可被序列化,可被默认的序列化机制序列化,不需要我们实现。

关于@Data注解

1、@Data注解是lombok.jar包下的注解,该注解通常用在实体bean上,不需要写出set和get方法,但是具备实体bean所具备的方法,简化编程提高变成速度。

2、@Data相当于@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode这5个注解的合集。

新建一个MybatisTest类用来测试

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
public class MybatisTest {
 
    public static void main(String[] args) {
        InputStream inputStream = null;
        SqlSessionFactoryBuilder builder = null;
        SqlSessionFactory sessionFactory = null;
        SqlSession session = null;
 
        try {
            // 读取mybatis-config.xml文件
            inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            // 创建工厂构建器
            builder = new SqlSessionFactoryBuilder();
            // 创建会话工厂
            sessionFactory = builder.build(inputStream);
            // 创建会话
            session = sessionFactory.openSession();
          // 通过映射文件中的namespace.id的形式找到相对应的方法
          //此处添加查询、插入、更改、删除等数据库操作代码
 
 
        }
        catch (Exception e) {
            session.rollback();//插入、删除等commit()修改数据库的操作要写回滚函数
            e.printStackTrace();
        }
        finally {
            // 关闭会话
            session.close();
        }
    }
}           

 注意:此时导入的方法必须都是与mybatis有关的

1
2
3
4
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.io.session.SqlSession;
import org.apache.ibatis.io.session.SqlSessionFactory;
import org.apache.ibatis.io.session.SqlSessionFactoryBuilder;

 单元测试方法

添加依赖

在pom.xml的<dependencies>标签中添加Junit相关依赖

1
2
3
4
5
6
<!--        junit单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
        </dependency>

 添加测试类

在src/test目录下添加要测试的类的同名+Test新建类,如:关于Class的测试ClassTest
并在要测试的方法前使用@Test
在测试类中,添加要构造函数,例如:Example example=new Example();

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ExampleTest {
    Example example=new Example();
    @Test
    public void add(){
        int s=example.add(10,20);
        System.out.println("s="+s);
    }
    @Test
    public void sum(){
        int sum =example.sum();
        System.out.println("sum="+sum);
    }
}

 数据库的简单操作

不带参数的单表查询

在MybatisTest类Main中,返回一条数据用selectOne()查询,Long型变量接收查询结果,例如:

1
2
// 通过映射文件中的namespace.id的形式找到相对应的方法
            Long count = session.selectOne("abc.queryCount");

 返回多条数据用selectList()查询,用List<Object>型变量接收查询结果,例如:

1
2
3
4
List<Emp> list = session.selectList("emp.dao.queryAllEmp");
            for (Emp emp : list) {
                System.out.println(emp.toString());
            }

 在select()系列函数中第一个参数是所在的页面通过映射文件中的namespace.id的形式,例如:

emp.dao.queryAllEmp,对应src/main/resources/mapper/EmpMapper.xml里的namespace和id,在<select>标签内写数据库查询语句:

1
2
3
4
5
6
<mapper namespace="emp.dao">
    <!-- 查询表中总记录数 -->
    <select id="queryAllEmp" resultType="Emp">
        select * from tb_emp
    </select>
</mapper>

 其中的返回的类型resultType则对应MybatisTest类main()中的接收参数的List<Object>类型的Object内的内容,例如List<Emp>

带参数的单表查询

selectOne()和selectList有两个参数,
第二个参数:可以传递的参数。
要传递单个参数直接填入,多个参数则需要将其写成一个对象或者使用hashMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//多参数传递
            Dept dept1 = new Dept();
            dept1.setDeptNo(1003);
            dept1.setDeptName("测试部");
            List<Dept> list1 = session.selectList("abc.queryDept", dept1);
//             map中key必须和映射文件中的占位符名称完全一样
            HashMap<String, Object> map = new HashMap<String, Object>();
            map.put("deptName","测试部");
            map.put("deptNo",1003);
            List<Dept> list2 = session.selectList("abc.queryDept", map);
 
            for (Dept dept : list1) {
                System.out.println(dept.toString());
            }

 在对应的映射文件src/main/resources/mapper/GradeMapper.xml中<select>标签内添加元素:parameterType,它的值对应select系列函数的第二参数的类型

1
2
3
4
5
6
//<select id="queryDept" resultType="Dept" parameterType="Dept">
<select id="queryDept" resultType="Dept" parameterType="Map">
        select * from tb_dept d
        where d.deptNo=#{deptNo}
        and d.deptName=#{deptName}
    </select>

 

多参数的插入

在MybatisTest类的Main中,用insert()函数写数据库插入代码,多参数传递使用Object或则HashMap类型(案例参照带参数的单表查询):

1
2
3
4
5
6
Dept dept = new Dept();
dept.setDeptNo(1004);
dept.setName("研发部")
session.insert("com.example.demo.Dept.addDept",dept);<br>       //插入、删除、修改等对数据库有影响的操作需要写commit(),来提交事务
session.commit();
System.out.println("操作成功");

 在对应的映射文件src/main/resources/mapper/GradeMapper.xml中<insert>标签内添加元素:parameterType,它的值对应insert函数的第二参数的类型

1
2
3
4
<insert id="addDept" parameterType="Dept">
    insert into tb_dept
    value (#{deptNo},#{deptName},#{remark})
</insert>

修改和删除

在MybatisTest类的Main中,用update()函数写数据库修改和删除代码:

1
2
3
4
5
6
7
8
9
//              修改
            Dept dept = new Dept();
            dept.setDeptNo(1004);
            dept.setName("研发部")
            session.update("com.example.demo.Dept.updateDept",dept);
            System.out.println("操作成功");
 
//              删除
            session.update("com.example.demo.Dept.delDept",1004);<br>        //插入、删除、修改等对数据库尊影响的操作需要写commit()来提交事务<br>       session.commit();<br>        System.out.println("操作成功");

 在对应的映射文件src/main/resources/mapper/GradeMapper.xml中<update>或<delete>标签内添加元素:parameterType,它的值对应update函数的第二参数的类型

1
2
3
4
5
<update id="updateDept" parameterType="Dept">
        update tb_dept d
set d.deptName=#{deptName},remark=#{remark}
    where d.deptNo=#{deptNo}
    </update>

 

1
2
3
4
<delete id="delDept" parameterType="int">
    delete from tb_dept
    where deptNo=#{deptNo}
</delete>

多表查询(一对多)

涉及到多个表的查询要对实体类进行修改,例如以tb_dept部门表作为主表,tb_emp员工表的deptNo作为外键,需要在Dept类中添加集合属性:

1
2
3
4
5
6
public class Dept implements Serializable {
    private Integer deptNo;
    private String deptName;
    private String deptRemark;
    private List<Emp> emps = new ArrayList<Emp>(); // 集合属性:多个员工
}

 然后更新get/set方法和toString方法;
在Emp类中添加映射对象属性:

1
2
3
4
5
6
7
public class Emp implements Serializable {
    private Integer empNo;
    private String empName;
    private Double salary;
    private Date hireDate;
   private Dept dept; // 映射对象属性
}

 并更新get/set 方法,但是toString()方法不需要重写。
在MybatisTest类的Main中写入查询相关代码

1
2
3
4
5
// 查询特定员工所在的部门
            List<Emp> list2 = session.selectList("emp.dao.queryDeptByEmp");
            for (Emp emp : list2) {
                System.out.println(emp.getEmpName() + "\t" + emp.getDept().getDeptName());}
            session.commit();

 

1
2
3
4
5
6
7
8
9
10
11
12
// 根据部门查询员工
            List<Dept> list1 = session.selectList("abc.queryEmpByDept");
            for (Dept dept : list1) {
                System.out.println(dept.toString());
 
                List<Emp> emps = dept.getEmps();
                for (Emp emp : emps) {
                    System.out.println(emp.toString());
                }
            }
            session.commit();
            System.out.println("==========操作成功");

 在对应的映射文件src/main/resources/mapper/GradeMapper.xml中写入二次映射实体对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 二次映射实体对象Dept -->
<resultMap id="deptMap" type="Dept">
    <id property="deptNo" column="deptNo"></id>
    <result property="deptName" column="deptName"></result>
    <result property="deptRemark" column="remark"></result>
 
    <!-- 集合属性的映射-->
    <collection property="emps" ofType="Emp" >
    <id property="empNo" column="empNo"></id>
    <result property="empName" column="empName"></result>
    <result property="salary" column="salary"></result>
    <result property="hireDate" column="hireDate"></result>
    </collection>
</resultMap>

 接着写入select查询语句结果返回值为二次映射对象id

1
2
3
4
5
6
7
8
9
10
<!-- 查询特定员工所在的部门-->
    <select id="queryDeptByEmp" resultMap="empMap">
        SELECT
            e.empName,d.deptName
        FROM
            tb_dept d,tb_emp e
        WHERE
            d.deptNo = e.deptNo
        AND e.empName = '李四'
    </select>

 在另一表对应的映射文件src/main/resources/mapper/EmpMapper.xml也写入二次映射实体对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
<resultMap id="empMap" type="Emp">
    <id property="empNo" column="empNo"></id>
    <result property="empName" column="empName"></result>
    <result property="salary" column="salary"></result>
    <result property="hireDate" column="hireDate"></result>
 
    <!-- 映射对象属性 -->
    <association property="dept" column="deptNo" javaType="Dept">
    <id property="deptNo" column="deptNo"></id>
    <result property="deptName" column="deptName"></result>
    <result property="deptRemark" column="remark"></result>
    </association>
</resultMap>

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="queryEmpByDept" resultMap="deptMap">
        SELECT
            e.empName,
            e.hireDate,
            d.deptName
        FROM
            tb_dept d,
            tb_emp e
        WHERE
            d.deptNo = e.deptNo
        AND d.deptName = '人事部'
    </select>
 
    <select id="queryDept" resultMap="deptMap">
        select * from tb_dept d
    </select>

 接着写入select查询语句结果返回值为二次映射对象id

至此需要实现的内容代码已经基本书写完成,接下来是代码优化,通过观察对比两个表的二次映射对象不难发现,其中有两端的代码重复,如tb_dept表的映射对象属性在tb_emp表的二次映射对象中重复tb_emp表也有相似的代码重复。
由于这两个映射文件都在src/main/resources/mybatis-config.xml中加在了

所以,这两映射文件可以添加resultMap={“”}属性相互引入,即

 

 

 

 

 

动态条件查询

 

<if>和<where>示例:

 

 

<if>和<set>示例:

 

 

 <trim>和<if>示例:

子查询

子查询示例:
在src/main/java/com/example/mybatisdemo/test/MybatisTest.java类的Main中

1
2
3
4
5
Integer[] ids={1001,1002};
List<Dept> list =session.selectList("abc.subQueryDept",ids);
for (Dept d :list){
    System.out.println(d.toString());
}

 在对应的映射文件src/main/resources/mapper/GradeMapper.xml中:

1
2
3
4
5
6
7
8
<select id="subQueryDept" resultMap="deptMap">
         //select * from tb_dept d where d.deptNo in(1001,1002)
        select * from tb_dept d
        where d.deptNo in
        <foreach collection="array" item="ids" open="(" close=")" separator=",">
            #{ids}
        </foreach>
    </select>

 其中除了用数组的形式,还可以用hashMap来写MybatisTest.java类中的查询代码:

1
2
3
4
5
6
7
8
9
10
11
            //子查询
            ArrayList<Integer> arys = new ArrayList<>();
            arys.add(1001);
            arys.add(1002);
            HashMap<String, List> ids = new HashMap<>();
            ids.put("array",arys);
//            Integer[] ids={1001,1002};
            List<Dept> list =session.selectList("abc.subQueryDept",ids);
            for (Dept d :list){
                System.out.println(d.toString());
            }

分页查询

分页查询示例:
在src/main/java/com/example/mybatisdemo/test/MybatisTest.java类的Main中

1
2
3
4
5
6
7
8
//            分页查询
            HashMap<String, Object> map = new HashMap<>();
            map.put("index",0);
            map.put("pageSize",2);
            List<Dept> list = session.selectList("abc.queryDeptByPage",map);
            for (Dept d : list){
                System.out.println(d.toString());
            }

 在对应的映射文件src/main/resources/mapper/GradeMapper.xml中:

1
2
3
4
<select id="queryDeptByPage" resultMap="deptMap" parameterType="map">
    select * from tb_dept
    limit #{index},#{pageSize}
</select>

模糊查询

方法1(通用):在src/main/java/com/example/mybatisdemo/test/MybatisTest类Main中

1
2
3
4
5
6
7
8
//模糊查询
            Dept dept2=new Dept();
            dept2.setDeptName("%"+"财"+"%");
            List<Dept> fuzzyDeptNameList = session.selectList("deptSpace.fuzzyQueryDeptName", dept2);
            System.out.println("模糊查询");
            for (Dept d : fuzzyDeptNameList){
                System.out.println(d.toString());
            }

  在对应的映射文件src/main/resources/mapper/GradeMapper.xml中:

1
2
3
4
<!--    方法2:仅限mysql使用concat-->
<!--select * from tb_dept where deptName like CONCAT('%','人','%');-->
    <select id="fuzzyQueryDeptName" resultMap="deptMap">
       select * from tb_dept where deptName like #{deptName}</select>

使用三层架构搭建项目

三层架构概述

1、三层架构包含的三层:

界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer)。


2、三层的职责:

界面层:接受用户的数据,调用业务逻辑层进行功能处理,返回结果给客户端,过去的 servlet 就是界面层的功能。

业务逻辑层:用来进行整个项目的业务逻辑处理,向上为界面层提供处理结果,向下问数据访问层要数据。

数据访问层:专门用来进行对数据的增、删、改、查操作,向上为业务逻辑层提供数据。


3、三层之间处理请求的交互:

(客户端)<--->界面层<--->业务逻辑层<--->数据访问层<--->(数据库)

各层之间的调用顺序是固定的,不允许跨层访问。

在mybatis框架结构下应用dao层

新建接口

新建一个包dao用来存储接口类,新建接口类(例如:DeptDao)

 

 

 添加操作数据库方法

将对应的数据库映射文件中的操作数据库语句的id写成函数名写入接口类中(例如:GradeMapper.xml中的数据库语句写入DeptDao)

1
2
3
4
5
6
7
8
9
10
11
/***
 * 部门管理数据访问接口
 */
public interface DeptDao {
     
public Long queryCount();
    public List<Dept> queryDept(Dept dept);
    /** //部门的查询
     Long deptCount = session.selectOne("deptSpace.queryCount");
     List<Dept> deptList = session.selectList("deptSpace.queryDept", dept);     */
}

 更改映射文件

更改映射文件的namespace为接口类的路径(例如:DeptDao和GradeMapper)

1
2
3
<!-- 在映射文件GradeMapper中-->
<!--<mapper namespace="deptSpace">-->
<mapper namespace="org.example.dao.DeptDao">

 更改关联表的映射文件中引用的该映射文件的namespce(例如:GradeMapper和EmpMapper)

1
2
3
4
<!--在关联表的映射文件EmpMapper中-->
<!--        集合属性映射-->
        <association property="dept" column="deptNo" javaType="Dept"
                     resultMap="org.example.dao.DeptDao.deptMap"></association>

使用DAO接口调用

在src/main/java/com/example/mybatisdemo/test/MybatisTest类Main中(例如:DeptDao)

1
2
3
4
5
6
7
8
9
10
11
//使用接口调用
 Dept dept=new Dept();
 dept.setDeptNo(1001);
 dept.setDeptName("财务部");
 dept.setRemark("");
 DeptDao deptDao = session.getMapper(DeptDao.class);
 List<Dept> depts = deptDao.queryDept(dept);
 System.out.println("接口的调用");
 for (Dept d : depts){
     System.out.println(d.toString());
 }

 整合Spring Boot

配置文件

在src/main/resources/application.yml,链接mysql数据库

1
2
3
4
5
6
7
#链接mysql数据库
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3307/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: 123456

 Pojo实体函数层

在java的主目录下新建po目录,并添加需要的数据库实体类(例如:Dept和Emp)

 

和在resource下添加对应的数据库映射文件

 

 Dao(DAL)数据访问层

在java的主目录下新建dao目录,并添加po目录下实体类对应的数据访问接口,

1
2
3
4
5
6
7
8
9
10
/***
 * 部门管理数据访问接口
 * 加注解
 */
@Mapper
public interface DeptDao {
    public Long queryCount();
    public List<Dept> queryEmpByDept();
public List<Dept> queryDept(Dept dept);
}

 例如DeptDao:
在src/main/resources/application.yml整合mybatis

 

1
2
3
4
5
6
7
8
9
#整合mybatis
mybatis:
  #加载实体类所在的包类
  type-aliases-package: springbootdemo.demo.po
  #加载映射文件
  mapper-locations: mapper/*.xml
  #把日志输出到控制台
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

 简单地测试数据访问

在src/test/java/springbootdemo/demo/DemoApplicationTests.java添加测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
//注入mapper
@Autowired
private DeptDao deptDao;
 
@Test
public void testQueryDept(){
    List<Dept> list = this.deptDao.queryDept(new Dept());
    System.out.println("测试成功");
    for (Dept d : list){
        System.out.println(d.toString());
    }
}

 点击测试,出结果测试成功

Service(BLL)业务逻辑层

在java的主目录下新建service目录,并添加po目录下实体类对应的业务逻辑接口和业务逻辑实现类

1
2
3
4
5
6
7
8
/***
 * 部门业务逻辑接口
 */
public interface DeptService {
        public Long queryCount();
        public List<Dept> queryEmpByDept();
        public List<Dept> queryDept(Dept dept);
}

 

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
/***
 * 部门业务逻辑实现
 */
@Service
@Transactional
public class DeptServiceImpl implements DeptService{
    //注入
    @Autowired
    private DeptDao deptDao;
 
    //实现方法
    //数据库的事务只有增删改,查不参与(特殊处理):Transactional
    @Override
//    @Transactional(Propagation.SUPPORTS)
    @Transactional(readOnly = true)
    public Long queryCount() {
        return this.deptDao.queryCount();
    }
 
    @Override
    @Transactional(readOnly = true)
    public List<Dept> queryEmpByDept() {
        return this.deptDao.queryEmpByDept();
    }
 
    @Override
    @Transactional(readOnly = true)
    public List<Dept> queryDept(Dept dept) {
        return this.deptDao.queryDept(dept);
    }
}

 Controller(UI)界面控制层(同步)

在java的主目录下新建controller目录,并添加po目录下实体类对应的界面控制类

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
/**
 * 同步,客户端和服务器端,
 * 页面提交请求,整个页面加载完才能做第二次操作,
 * 整个页面的完整刷新,要有等待
 *
 */
@Controller
//异步,页面提交请求,服务器没有响应,依然可以二次操作,局部刷新
//@RestController
public class DeptController {
    //注入
    @Autowired
    private DeptService deptService;
     
    //识别方法
    @RequestMapping("/index")
    //RequestMapping的细化
//    @GetMapping("/index")
//    @PostMapping("/index")
    public String index(Model model){
 
        List<Dept> list = this.deptService.queryDept(new Dept());
        System.out.println("查询成功");
        for (Dept d : list){
            System.out.println(d.toString());
        }
        model.addAttribute("list",list);
        //返回页面的名称(返回的字符串必须和页面的名称相同)
        return "index";//index.html默认已经添加后缀
    }
    @RequestMapping("/query")
    public String query(){
        return "list";//错误示例
    }
}

 在src/main/resources/templates中对应的 HTML文件(例如index.html和list.html)
并在其<html>标签中添加 xmlns:th="http://www.thymeleaf.org"建立映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
 
<body>
<h1>首页</h1>
 
<table>
    <tr>
        <th>编号</th>
        <th>名称</th>
        <th>备注</th>
    </tr>
    <tr th:each="dept:${list}">
        <td th:text="${dept.deptNo}"></td>
        <td th:text="${dept.deptName}"></td>
        <td th:text="${dept.remark}"></td><br>     <td><br>            <a th:href="@{/del(deptNo=${dept.deptNo})}">删除</a><br>            <a th:href='@{/gotoEdit/}+${dept.deptNo}'>修改</a><br>        </td>
    </tr>
</table>
</body>
</html>

 

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
 
    <title>list</title>
</head>
<body>
    <p>列表</p>
</body>
</html>

 最后在java主函数中添加注解

1
//开启事务管理<br> @EnableTransactionManagement

 Controller(UI)界面控制层(异步)

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
//异步调用
    @RequestMapping("/ajaxQuery")
    @ResponseBody
    @ApiOperation("异步查询部门")
    public List<Dept> ajaxQuery(){
        List<Dept> list = this.deptService.queryDept(new Dept());
        return list;
    }
 
    @RequestMapping("/ajaxQueryAll")
    @ResponseBody
    @ApiOperation("异步查询员工")
    public List<Dept> ajaxQueryAll(){
        List<Dept> list = this.deptService.queryAllDept(new Dept());
        return list;
    }
 
    @RequestMapping("/ajaxSaveDept")
    @ResponseBody
    @ApiOperation("异步保存添加部门")
    public String ajaxSaveDept(@RequestBody Dept dept){
        this.deptService.addDept(dept);
        return "success";
    }
 
    @RequestMapping("/ajaxDelDept/{deptNo}")//开启事务管理
    @ResponseBody
    @ApiOperation("异步删除部门")
    public String ajaxDelDept(@PathVariable String deptNo){
        this.deptService.delDept(Integer.valueOf(deptNo));
        return "success";
    }
 
    @GetMapping("/ajaxQueryDept/{deptNo}")
    @ResponseBody
    @ApiOperation("异步根据编码查询部门")
    public Dept ajaxQueryDept(@PathVariable String deptNo){
        Dept dept = new Dept();
        dept.setDeptNo(Integer.valueOf(deptNo));
        List<Dept> list = this.deptService.queryDept(dept);
        return list.get(0);
    }
 
    @PostMapping("/ajaxUpdateDept")
    @ResponseBody
    @ApiOperation("异步删除部门")
    public String ajaxUpdateDept(@RequestBody Dept dept){
        this.deptService.updateDept(dept);
        return "success";
    }

在src/config 新建一个CorsFilter类,用作跨域处理

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
import org.springframework.context.annotation.Configuration;
 
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
 
/**
 * 跨域过滤器,解决前后端分离时前端项目的跨域请求报错
 */
@Configuration
public class CorsFilter implements Filter {
 
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;
 
        // 不使用*,自动适配跨域域名,避免携带Cookie时失效
        String origin = request.getHeader("Origin");
        if (origin != null && !origin.equals("")) {
            response.setHeader("Access-Control-Allow-Origin", origin);
        }
 
        // 自适应所有自定义头
        String headers = request.getHeader("Access-Control-Request-Headers");
        if (headers != null && !headers.equals("")) {
            response.setHeader("Access-Control-Allow-Headers", headers);
            response.setHeader("Access-Control-Expose-Headers", headers);
        }
 
        // 允许跨域的请求方法类型
        response.setHeader("Access-Control-Allow-Methods", "*");
        // 预检命令(OPTIONS)缓存时间,单位:秒
        response.setHeader("Access-Control-Max-Age", "3600");
        // 明确许可客户端发送Cookie,不允许删除字段即可
        response.setHeader("Access-Control-Allow-Credentials", "true");
 
        chain.doFilter(request, response);
    }
 
}

 配置application文件

更改服务器端口

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
#链接mysql数据库
#com.mysql.jdbc.Driver高版本数据库链接要添加cj:com.mysql.cj.jdbc.Driver
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
 
  mvc:
    format:
      date: yyyy-MM-dd
 
 
  jackson:
    date-format: yyyy-MM-dd
    time-zone: Asia/Shanghai
#整合mybatis
mybatis:
  #加载实体类所在的包类
  type-aliases-package: springbootdemo.demo.po
  #加载映射文件
  mapper-locations: mapper/*.xml
  #把日志输出到控制台
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
 
server:
  port: 8888

处理跨域请求

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
import org.springframework.context.annotation.Configuration;
 
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
 
/**
 * 跨域过滤器,解决前后端分离时前端项目的跨域请求报错
 */
@Configuration
public class CorsFilter implements Filter {
 
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;
 
        // 不使用*,自动适配跨域域名,避免携带Cookie时失效
        String origin = request.getHeader("Origin");
        if (origin != null && !origin.equals("")) {
            response.setHeader("Access-Control-Allow-Origin", origin);
        }
 
        // 自适应所有自定义头
        String headers = request.getHeader("Access-Control-Request-Headers");
        if (headers != null && !headers.equals("")) {
            response.setHeader("Access-Control-Allow-Headers", headers);
            response.setHeader("Access-Control-Expose-Headers", headers);
        }
 
        // 允许跨域的请求方法类型
        response.setHeader("Access-Control-Allow-Methods", "*");
        // 预检命令(OPTIONS)缓存时间,单位:秒
        response.setHeader("Access-Control-Max-Age", "3600");
        // 明确许可客户端发送Cookie,不允许删除字段即可
        response.setHeader("Access-Control-Allow-Credentials", "true");
 
        chain.doFilter(request, response);
    }
 
}

 

使用 Swagger测试API

SwaggerConfig配置类

在src/config/目录下新建SwaggerConfig类

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
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
 
@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket getRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .pathMapping("/")
                .select()
                // 扫描自定义控制器所在的包路径
                .apis(RequestHandlerSelectors.basePackage("com.example.elicense.controller"))
                .paths(PathSelectors.any())
                // 构建swagger首页的自定义信息
                .build().apiInfo(new ApiInfoBuilder()
                        .title("SpringBoot整合Swagger")
                        .description("SpringBoot整合Swagger,详细信息......")
                        .version("1.0")
                        .contact(new Contact("xphohr", "http://www.cnblogs.com/linlinmailbox", "1521470143@qq.com"))
                        .license("The Apache License")
                        .licenseUrl("https://www.apache.org/")
                        .build());
    }
}

添加注解

 在每一个视图控制器Controller里添加注解@Api(tags=""), @ApiOperation(""),    @ApiImplicitParams({ @ApiImplicitParam(name = "",value = "",dataType = ""),})

例如:

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
/**
 * 视图控制层:元数据
 */
@Controller
@RequestMapping("/item")
@Api(tags="元数据管理控制器")
public class ItemController {
    //自动装配 业务逻辑层
    @Autowired
    private ItemService itemService;
 
    /**
     * 分页查询
     *
     * @param pageNo
     * @param pageSize
     * @return
     */
    @GetMapping("/all")
    @ResponseBody
    @ApiOperation("分页查询")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "pageNo",value = "页码",dataType = "int"),
            @ApiImplicitParam(name = "pageSize",value = "页面大小",dataType = "int"),
    })
    public List<Item> queryAllItem(@RequestParam int pageNo, @RequestParam int pageSize) {
        return this.itemService.queryItem(pageNo, pageSize);
    }
 
    /**
     * 条件查询
     *
     * @param paramsName
     * @param paramsValue
     * @return
     */
    @GetMapping("/{paramsName}/{paramsValue}")
    @ResponseBody
    @ApiOperation("条件查询")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "paramsName",value = "参数名",dataType = "String"),
            @ApiImplicitParam(name = "paramsValue",value = "参数值",dataType = "String"),
    })
    public List<Item> queryItemsByItem(@PathVariable String paramsName, @PathVariable String paramsValue) {
        return this.itemService.queryItemCondition(paramsName, paramsValue);
    }
 
    /**
     * 新增元数据
     *
     * @param item
     * @return
     */
    @PostMapping("/add")
    @ResponseBody
    @ApiOperation("新增元数据")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "item",value = "元数据信息对象",dataType = "Item"),
    })
    public String addItem(@RequestBody Item item) {
        this.itemService.insertItem(item);
        return "success";
    }
 
    /**
     * 修改元数据信息
     *
     * @param item
     * @return
     */
    @PostMapping("/update")
    @ResponseBody
    @ApiOperation("修改元数据信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "item",value = "模板信息对象",dataType = "Item"),
    })
    public String updateItem(@RequestBody Item item) {
        this.itemService.updataItem(item);
        return "success";
    }
 
    /**
     * 删除元数据信息
     *
     * @param itemNo
     * @return
     */
    @PostMapping("/del/{itemNo}")
    @ResponseBody
    @ApiOperation("删除元数据信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "itemNo",value = "模板编号",dataType = "int"),
    })
    public String delItem(@PathVariable Integer itemNo) {
        this.itemService.deleteItemByItemNo(itemNo);
        return "success";
    }
}

 

未完待续。。。

 

posted @   霖霖的信箱  阅读(80)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示