0%

说明

  • Spring Boot让我们的Spring应用变的更轻量化
  • 我们不必像以前那样繁琐的构建项目、打包应用、部署到Tomcat等应用服务器中来运行我们的业务服务。
  • 通过Spring Boot实现的服务,只需要依靠一个Java类,把它打包成jar,并通过java -jar命令就可以运行起来。
  • 依赖说明
1
2
3
4
5
1. Maven  需求:3.5+
2. JDK 需求 8+
3. Spring Framework 5.3.7以上版本
4. Tomcat 9.0
5. Servlet版本 4.0 但是可以部署到Servlet到3.1+的容器中

快速入门

  • 依赖文件
1
2
3
4
5
6
<!--springboot的父工程其中定义了常用的依赖,并且无依赖冲突-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
</parent>

注意上方的parent必须加,其中定义了springboot官方支持的n多依赖,基本上常用的已经有了,所以接下来导入依赖的时候,绝大部分都可以不加版本号。

  • 添加web依赖
1
2
3
4
5
6
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

添加上方的web依赖,其中间接依赖了spring-web,spring-webmvc,spring-core等spring和springmvc的包,并且集成了tomcat。

  • 编写启动类.src-main-java-对应的包 目录下
1
2
3
4
5
6
7
8
9
10
11
package xyz.shi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HelloApp {

public static void main(String[] args) {
SpringApplication.run(HelloApp.class,args);
}
}

@SpringBootApplication注解标识了HelloApp为启动类,也是Spring Boot的核心。

  • 运行后发现下面的日志,tomcat端口为8080

image-20240222104341082

  • 如果想要修改端口号,可以在resources目录下新建application.properties,然后重写运行即可
1
server.port=8082

编写一个Http接口

  • 创建HelloController类,内容如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package xyz.shi.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("hello")
public class HelloController {

@GetMapping("boot")
public String hello(){
return "hello spring boot";
}

}
  • 重启程序,使用postman或者直接在浏览器输入http://localhost:8082/hello/boot

image-20240222104901876

单元测试

  • 加入依赖
1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
  • 在src/test/java/xyz.shi 下,编写测试用例
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
package xyz.shi;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import xyz.shi.controller.HelloController;

import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
public class TestHelloController {

private MockMvc mockMvc;

@BeforeEach
public void beforeEach() {
mockMvc = MockMvcBuilders.standaloneSetup(new HelloController()).build();
}

@Test
public void testHello() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/hello/boot")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("hello spring boot")));
}
}

对Controller进行单元测试,需要用到mock

测试类的包需要和启动类HelloApp的包一致

打包为jar运行

  • 添加打包(maven构建springboot)插件
1
2
3
4
5
6
7
8
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
  • 打开idea中的project Structure,选中Artifacts,点击中间的加号(Project Settings->Artifacts->JAR->From modules with dependencies ),如下图所示:

image-20240222171952272

image-20240222172921801

  • 开始打包,点击右侧的Maven Projects,打开LIfecycle,先点击clean,再点击package,生成target文件夹,里面有以项目名命名加版本号的jar文件,至此打包完成。

image-20240222174210640

  • ,IDEA会自动开始把项目所有需要的文件和依赖包到打包到一个jar里面,完成后左侧目录栏里的target文件下会出现两个神奇的文件

image-20240222175201164

  • 进入jar所在的文件夹,使用java -jar命令运行jar,项目就能启动
1
2
3
4
5
6
7
8
9
10
Administrator@WIN-20230710BAT MINGW64 /d/project/StudySpringBoot/target
$ java -jar StudySpringBoot-1.0-SNAPSHOT.jar

. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/

  • 测试,使用postman或者直接在浏览器输入http://localhost:8082/hello/boot

  • 得到结果:hello spring boot

  • jar tvf XXX.jar 可以查看包的内容

工程结构

配置文件详解

整合MybatisPlus

  • 依赖文件
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
<dependencies>
<!--web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mybatis -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

</dependencies>
  • resources-application.properties 配置数据库信息
1
2
3
4
5
6
server.port=8082

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdb
  • 实体类
1
2
3
4
5
6
7
8
9
10
11
package xyz.shi.pojo;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@TableName("users")
@Data
public class User {
private Integer id;
private String name;
private String password;
}
  • mapper
1
2
3
4
5
6
7
package xyz.shi.mapper;

import xyz.shi.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UserMapper extends BaseMapper<User> {
}

  • config扫描mapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package xyz.shi.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("xyz.shi.mapper")
public class MybatisPlusConfig {

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}

  • service
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
package xyz.shi.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import xyz.shi.mapper.UserMapper;
import xyz.shi.pojo.User;
import java.util.List;

@Service
@Slf4j
public class UserService {

@Autowired
private UserMapper userMapper;

public List<User> findAll() {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
List<User> users = userMapper.selectList(queryWrapper);
return users;
}

//分页
public List<User> findPage(Integer CurrentPage, Integer PageSize) {
System.out.println(CurrentPage);
System.out.println(PageSize);
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
Page page = new Page(CurrentPage, PageSize);
Page<User> userPage = userMapper.selectPage(page, queryWrapper);
log.info("total:{}", userPage.getTotal());
log.info("pages:{}", userPage.getPages());
return userPage.getRecords();
}
}
  • resource-logback-spring.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"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>logback</contextName>
<!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
<!-- 定义日志存储的路径,不要配置相对路径 -->
<!-- <property name="FILE_PATH" value="/Users/Adm/spring-log.%d{yyyy-MM-dd}.%i.log" />-->
<property name="FILE_PATH" value="d:/log/spring-log.%d{yyyy-MM-dd}.%i.log" />
<!-- 控制台输出日志 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志级别过滤INFO以下 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<!-- 按照上面配置的LOG_PATTERN来打印日志 -->
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<!--每天生成一个日志文件,保存30天的日志文件。rollingFile用来切分文件的 -->
<appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${FILE_PATH}</fileNamePattern>
<!-- keep 15 days' worth of history -->
<maxHistory>30</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- 日志文件的最大大小 -->
<maxFileSize>2MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- 超出删除老文件 -->
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- logger节点,可选节点,作用是指明具体的包或类的日志输出级别,
以及要使用的<appender>(可以把<appender>理解为一个日志模板)。
addtivity:非必写属性,是否向上级loger传递打印信息。默认是true-->
<logger name="net.sh.rgface.serive" level="ERROR" />
<!--项目的整体的日志打印级别为info-->
<root level="INFO">
<appender-ref ref="console" />
<appender-ref ref="rollingFile" />
</root>
</configuration>
  • UserController
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
package xyz.shi.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xyz.shi.pojo.User;
import xyz.shi.service.UserService;
import java.util.List;

@RestController
@RequestMapping("user")
public class UserController {

@Autowired
private UserService userService;

@GetMapping("findAll")
public List<User> findAll() {
return userService.findAll();
}

@GetMapping("findPage")
public List<User> findPage(Integer CurrentPage, Integer PageSize) {
return userService.findPage(CurrentPage, PageSize);
}
}
  • 启动后浏览器访问 http://localhost:8082/user/findPage?CurrentPage=1&PageSize=1

image-20240223165729058

  • 日志文件

image-20240223165819078

总结

  • spring系列暂时告一段落,后面开展学习vue方面的知识并且结合springboot

说明

  • 本次搭建环境信息,idea社区版、java1.8、mysql8x,本地tomcat9.0.83

  • idea装好smart tomcat并且关联本地的tomcat

ssm整合

  • s: springMVC,s: spring,m: mybatis,上述三个框架合起来,称为ssm,所谓的ssm整合说的就是将三个框架集成起来

  • 依赖文件

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
<dependencies>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<!--mybatis环境-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!--spring整合mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<!--连接池-->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!--分页插件坐标-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<!--servlet环境-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

集成spring

  • 在resources下新建applicationContext.xml,做为spring的配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<!--开启bean注解扫描-->
<context:component-scan base-package="xyz.shi"/>

</beans>
  • 新建UserService.java
1
2
3
4
5
6
7
8
9
10
11
package xyz.shi.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
public void test(){
System.out.println("test....");
}
}

  • 新建APP.java,测试UserService可用
1
2
3
4
5
6
7
8
9
10
11
12
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xyz.shi.service.UserService;

public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean(UserService.class);
userService.test();
}
}

Spring集成Mybatis

  • 实体类
1
2
3
4
5
6
7
8
9
10
package xyz.shi.pojo;

import lombok.Data;

@Data
public class User {
private int id;
private String name;
private String password;
}
  • 在resources新建jdbc.properties,在spring-dao.xml中引入,将数据库配置提取出来
1
2
3
4
5
6
7
jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.autoCommit=true
jdbc.connectionTimeout=5000
jdbc.idleTimeout=60
  • 在resources下新建spring-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
34
35
36
37
38
39
40
41
42
43
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath*:*.properties"/>

<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="autoCommit" value="${jdbc.autoCommit}"/>
<property name="connectionTimeout" value="${jdbc.connectionTimeout}" />
<property name="idleTimeout" value="${jdbc.idleTimeout}" />
</bean>
<!--jdbc的xml配置-->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<constructor-arg name="configuration" ref="hikariConfig"/>
</bean>

<!--mybatis-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath*:mapper/*.xml"/>
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<props>
<prop key="helperDialect">mysql</prop>
</props>
</property>
</bean>
</array>
</property>
</bean>
<!--配置mapper接口的扫包路径,目的是代理此接口,生成代理类 注册到spring容器中-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="xyz.shi.mapper"/>
</bean>
</beans>
  • 新建UserMapper接口
1
2
3
4
5
6
7
package xyz.shi.mapper;

import xyz.shi.pojo.User;
public interface UserMapper {
User findUser(Integer id);
}

  • 在resources下新建mapper/UserMapper.xml
1
2
3
4
5
6
7
8
9
10
<?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 namespace="xyz.shi.mapper.UserMapper">

<select id="findUser" parameterType="int" resultType="xyz.shi.pojo.User">
select * from users where id=#{id}
</select>
</mapper>
  • 在UserService中注入UserMapper,实现查询用户的业务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package xyz.shi.service;

import org.springframework.stereotype.Service;
import xyz.shi.mapper.UserMapper;
import xyz.shi.pojo.User;

import javax.annotation.Resource;

@Service
public class UserService {
@Resource
private UserMapper userMapper;
public void test(){
System.out.println("test....");
}
public User getUser(int id){
return userMapper.findUser(id);
}
}

集成SpringMVC

将pom.xml中的package改为war,加入springMVC,就是web工程,同时导入spring-webmvc的依赖

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"?>
<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>org.example</groupId>
<artifactId>ssm</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>

<!-- <dependency>-->
<!-- <groupId>org.springframework</groupId>-->
<!-- <artifactId>spring-context</artifactId>-->
<!-- <version>5.2.16.RELEASE</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
  • 在main的下面,新建webapp目录

  • webapp下新建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
31
32
33
34
35
36
37
38
39
40
41
42
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--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>
<!--解决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>
<!--springmvc配置文件加载-->
<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*:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!--/代表拦截所有请求,一般也写做*.do代表只匹配.do后缀的请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>
  • 在resources下新建spring-mvc.xml,其中加入mvc相关的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--这个不能忘,分开加载spring配置文件,扫包也需要单独配置-->
<context:component-scan base-package="xyz.shi.controller" />
<!--开启mvc的注解支持-->
<mvc:annotation-driven/>

<!--扫包的配置,在spring配置文件中 已经定义,保证controller在扫包范围内即可-->

<!--过滤静态文件-->
<mvc:default-servlet-handler />
</beans>
  • 新建UserController.java,调用Service,完成根据id获取用户信息的逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package xyz.shi.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xyz.shi.pojo.User;
import xyz.shi.service.UserService;

@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;

@GetMapping("getUser/{id}")
public User findUser(@PathVariable int id){
return userService.getUser(id);
}
}

  • 本地安装tomcat,我的版本为9.0.83,然后idea中装插件Smart tomcat,然后webapp右键运行
  • http://localhost:8080/ssm/user/getUser/4 打开

image-20240220151657257

添加事务配置

resource-spring-tx.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<tx:annotation-driven proxy-target-class="true"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
  • applicationContext.xml中导入
1
<import resource="spring-tx.xml" />
  • usermapper.xml加入新增和查询所有
1
2
3
4
5
6
7
<insert id="save" parameterType="xyz.shi.pojo.User" keyProperty="id" useGeneratedKeys="true">
insert into users(name,password)
value(#{name},#{password})
</insert>
<select id="findAll" resultType="xyz.shi.pojo.User">
select * from users
</select>
  • usermapper代码,加入了新增方法
1
2
3
4
5
public interface UserMapper {
User findUser(int id);
void saveTx(User user);
Page<User> findAll();
}
  • UserService
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
package xyz.shi.service;

import org.springframework.stereotype.Service;
import xyz.shi.mapper.UserMapper;
import xyz.shi.pojo.User;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

@Service
public class UserService {
@Resource
private UserMapper userMapper;
public void test(){
System.out.println("test....");
}
public User getUser(int id){
return userMapper.findUser(id);
}
public Page<User> findAll(Integer PageNum, Integer PageSize) {
//在查询之前,设置分页条件 显示第一页,展示3条数据
Page<Object> page = PageHelper.startPage(PageNum, PageSize);
return userMapper.findAll();
}

@Transactional
public void saveTx(User user) {
userMapper.save(user);
int i = 10 / 0; //模拟发生异常
// 加上事务标识后,只有出现异常才不会造成新增数据成功
}

加上事务标识后,只有出现异常才会触发事务

  • UserController
1
2
3
4
5
6
7
@GetMapping("saveTx")
public void saveTx(String name, String password) {
User user = new User();
user.setName(name);
user.setPassword(password);
userService.saveTx(user);
}
  • 打开http://localhost:8080/ssm/user/saveTx?name=123&&password=456 报错了,数据新增不会成功,如果不加Transactional标识,即使报错了新增数据也会成功
  • 分页查询

image-20240221103126485

其他

拦截器

  • 在springmvc文章中已经实践

统一异常处理

为什么要有统一异常呢?

  • 如果是编码过程中没有预料到的异常,比如bug,内存不足,硬件错误等等造成的,会提示给用户一些不友好的信息,如果在controller中处理,那么每个controller都要try catch,又过于繁琐
  • 异常的种类很多,有自定义的业务异常,有系统异常,有空指针异常,有数组越界等,每种异常处理的方式都不一致,比如业务异常,需要提示用户准确信息,系统异常需要提示用户友好信息(系统繁忙,请稍候再试)并且记录错误,空指针异常遇到发送错误日志短信给开发人员等

使用

  • handle包下有分别为异常实体类、自定义异常等
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
package xyz.shi.handler;
import lombok.Data;
//统一结果返回
@Data
public class Result {
private boolean success;
private int code;
private String message;
private Object data;
}

package xyz.shi.handler;
public class BusinessException extends RuntimeException {
private int code;
public BusinessException(){
super();
}
public BusinessException(Integer code,String message){
super(message);
this.code = code;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}

package xyz.shi.handler;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public Result doException(Exception e) {
//记录异常
e.printStackTrace();
Result result = new Result();
result.setCode(-999);
result.setMessage("未知的异常,提示友好信息");
result.setSuccess(false);
return result;
}

@ExceptionHandler(IndexOutOfBoundsException.class)
@ResponseBody
public Result doStack(IndexOutOfBoundsException e) {
//记录异常
e.printStackTrace();
//发送短信给开发人员
// send()
Result result = new Result();
result.setCode(-999);
result.setMessage("数组越界异常");
result.setSuccess(false);
return result;
}

@ExceptionHandler(BusinessException.class)
@ResponseBody
public Result doBusiness(BusinessException e) {
Result result = new Result();
result.setCode(e.getCode());
result.setMessage(e.getMessage());
result.setSuccess(false);
return result;
}
}
  • 控制层修改代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 @GetMapping("getUser/{id}")
public Result findUser(@PathVariable int id){
System.out.println("getUser方法调用...");
if (id == 2){
throw new BusinessException(-999,"对不起,参数不能为2");
}
if (id == 3){
throw new IndexOutOfBoundsException();
}
if (id == 4){
int i = 10/0;
}
User user = userService.getUser(id);
return new Result(true,200,"success",user);
}

image-20240221112245723

  • 获取值正常时,比如:http://localhost:8080/ssm/user/getUser/10,得到结果{"success":true,"code":200,"message":"success","data":{"id":10,"name":"456","password":"123456"}}

数据校验

在进行请求的时候,大多数往往会携带参数,我们总需要对参数进行合法性的校验,比如不能为null,密码长度不能小于8,用户名必须使用大小写字母+数字等等

JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,通过 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证。

image-20240221114212311

  • 依赖包
1
2
3
4
5
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>
  • 实体类修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package xyz.shi.pojo;

import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;

@Data
public class User {
private int id;
@NotNull(message = "用户名不能为空")
private String name;
@Length(min = 5, max = 100, message = "密码长度只能在5-100之间")
private String password;
}
  • 控制层新增一个post新增数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@PostMapping("save")
public Result save( @Valid User user, BindingResult bindingResult) {
System.out.println(user);
if (bindingResult.hasErrors()) {
//获取校验有错误的字段
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
System.out.println(fieldError.getField());
System.out.println(fieldError.getDefaultMessage());
System.out.println("------------------------------");
return new Result(true,-1,fieldError.getDefaultMessage(),null);

}
}
userService.save(user);
return new Result(true,200,"success",user);
}
  • python模拟发一个post请求
1
2
3
4
5
6
data = {"name": "123", "password": "1111"}
t = requests.post("http://localhost:8080/ssm/user/save", data=data)
s = t.text
print(s)
==
{"success":true,"code":-1,"message":"密码长度只能在5-100之间","data":null}

日志

  • 主流使用的日志是logback和log4j2,本次主要演示logback
  • 依赖文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.logback-extensions</groupId>
<artifactId>logback-ext-spring</artifactId>
<version>0.1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>
  1. 第一个logback-classic包含了logback本身所需的slf4j-api.jarlogback-core.jarlogback-classsic.jar
  2. 第二个logback-ext-spring是由官方提供的对Spring的支持
  3. 第三个jcl-over-slf4j是用来把Spring源代码中大量使用到的commons-logging替换成slf4j,只有在添加了这个依赖之后才能看到Spring框架本身打印的日志–即info文件中打印出的spring启动日志信息,否则只能看到开发者自己打印的日志
  • 在resources下创建logback.xml
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<!--scan当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。scanPeriod 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。debug 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。-->
<configuration scan="true" scanPeriod="60 seconds" debug="true">
<!-- 模块名称, 影响日志配置名,日志文件名 -->
<property name="appName" value="mszluSpring"/>
<property name="logMaxSize" valule="100MB"/>
<!--rootPath 日志路径 -->
<property name="rootPath" value="D:/log"/>
<contextName>${appName}</contextName>
...

日志级别标准:ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF

  • 直接运行后,就能在d/log目录下看到相应的日志文件
  • 但是发现一个问题,如果系统报错了error日志没有记录,是因为我们使用了@ControllerAdvice这个异常类,需要在自定义异常类中加入如下代码:log.error
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
package xyz.shi.handler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class MyExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(MyExceptionHandler.class);
@ExceptionHandler(Exception.class)
@ResponseBody
public Result doException(Exception e) {
//记录异常
e.printStackTrace();
log.error("500 Internal Server Error", e);

Result result = new Result();
result.setCode(-999);
result.setMessage("未知的异常,提示友好信息");
result.setSuccess(false);
return result;
}

@ExceptionHandler(IndexOutOfBoundsException.class)
@ResponseBody
public Result doStack(IndexOutOfBoundsException e) {
//记录异常
e.printStackTrace();
log.error("500 Internal Server Error", e);
//发送短信给开发人员
// send()
Result result = new Result();
result.setCode(-999);
result.setMessage("数组越界异常");
result.setSuccess(false);
return result;
}

@ExceptionHandler(BusinessException.class)
@ResponseBody
public Result doBusiness(BusinessException e) {
log.error("500 Internal Server Error", e);
Result result = new Result();
result.setCode(e.getCode());
result.setMessage(e.getMessage());
result.setSuccess(false);
return result;
}
}

纯注解ssm整合

  • 就是去掉web.xml 这些配置,使用配置类的方式,我实践了一下,无法启动成功,可能是我用的idea社区版本

  • 参考步骤

说明

  • 本篇开始学习springmvc的知识
  • win10,idea社区版本,java1.8

SpringMVC

  • Spring MVC 本身就是 Spring 框架的一部分,可以说和 Spring 框架是无缝集成。性能方面具有先天的优越性,是当今业界最主流的 Web 开发框架

MVC

  • 控制层(Controller):负责接收并转发请求,对请求进行处理后,指定视图并将响应结果发送给客户端。
  • 视图层(View):负责格式化数据并把它们呈现给用户,包括数据展示、用户交互、数据验证、界面设计等功能。
  • 数据模型层(Model):模型对象拥有最多的处理任务,是应用程序的主体部分,它负责数据逻辑(业务规则)的处理和实现数据操作(即在数据库中存取数据)

执行流程

image-20240218091941742

实例

  • 依赖文件如下
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

<packaging>war</packaging>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>5.2.16.RELEASE</spring.version>

</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!-- Spring Test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<!-- servlet api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>

</project>

将pom.xml中的package改为war,加入springMVC,就是web工程

war 这里默认是pom,我改成了war,经过测试没有区别

  • 在src->main下新建webapp目录

  • 在webapp下新建WEB-INF目录,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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--编码过滤器,解决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>

<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*:spring-mvc.xml</param-value>
</init-param>
<!--启动级别 1 表示服务器启动,这个项目也跟着启动-->
<load-on-startup>1</load-on-startup>
</servlet>


<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!--,所有的请求都需要通过DispatcherServlet -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 启用Spring的Session -->
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

</web-app>

发现报错:URI is not registered (Settings | Languages & Frameworks | Schemas and DTDs)

在idea中按照报错的步骤把http://xmlns.jcp.org/xml/ns/javaee 加入进去即可

  • 上方的配置文件,需要加载spring的配置文件,在resources下新建spring-mvc.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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--扫包,spring可以识别此包以及子包下的类中的spring相关注解,比如Service,Controller,Component等-->
<context:component-scan base-package="xyz.shi" />
<!-- 配置@controller扫描包 -->
<!--<context:component-scan base-package="cn.book.controller" />-->
<context:annotation-config/>
<!-- 配置注解驱动,相当于同时使用最新处理器映射跟处理器适配器,对json数据响应提供支持 -->
<mvc:annotation-driven enable-matrix-variables="true"/>
<!--对webapp下的静态文件放行,不去走中央控制器-->
<mvc:default-servlet-handler />

<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&amp;allowMultiQueries=true&amp;serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- 配置jdbc -->

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事务管理器 -->
<!-- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">-->
<!-- <property name="dataSource" ref="dataSource"/>-->
<!-- </bean>-->
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/users/**"/>
<bean class="xyz.shi.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

</beans>
  • dao层
1
2
3
4
5
6
7
8
9
public interface UserDao {
boolean login(User user);
void save(User user);
void delete(int id);
void update(User user);
User findById(int id);
List<User> findAll();
}

  • 实现dao,这里就是和数据库直接对接
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
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public boolean login(User user) {
String sql = "SELECT * FROM users WHERE name = ? AND password = ?";
List<User> users = jdbcTemplate.query(sql, new Object[]{user.getName(), user.getPassword()},
BeanPropertyRowMapper.newInstance(User.class));
if (!users.isEmpty()) {
return true;
} else {
return false;
}
}

@Override
public void save(User user) {
String sql = "INSERT INTO users (name, password) VALUES (?, ?)";
jdbcTemplate.update(sql, user.getName(), user.getPassword());
}

@Override
public void delete(int id) {
String sql = "DELETE FROM users WHERE id = ?";
jdbcTemplate.update(sql, id);
}

@Override
public void update(User user) {
String sql ="update `users` set name = ?,password = ? where id = ? ";
jdbcTemplate.update(sql, user.getName(), user.getPassword(), user.getId());
}

@Override
public User findById(int id) {
String sql = "SELECT * FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new Object[]{id},
BeanPropertyRowMapper.newInstance(User.class));
}

@Override
public List<User> findAll() {
String sql = "SELECT * FROM users";
// 查询数据
List<User> userList = jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(User.class));
return userList;
}
}
  • service层
1
2
3
4
5
6
7
8
public interface UserService {
boolean login(User user);
void save(User user);
void delete(int id);
void update(User user);
User findById(int id);
List<User> findAll();
}
  • 实现service,这里主要作用实现service层的接口,具体数据逻辑是调用的userdao层,而userdao自动关联到了他的实现层也就是UserDaoImpl
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
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public boolean login(User user) {
return userDao.login(user);
}
@Override
public void save(User user) {
userDao.save(user);
}
@Override
public void delete(int id) {
userDao.delete(id);
}
@Override
public void update(User user) {
userDao.update(user);
}
@Override
public User findById(int id) {
return userDao.findById(id);
}
@Override
public List<User> findAll() {
return userDao.findAll();
}
}
  • Controller层的登录 主要用来处理登录和注销
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

@Controller
@SessionAttributes("username")
@RequestMapping("/home")
public class LoginController {
@Autowired
private UserDao userDao;

@GetMapping("/userlogin")
public String userlogin() {
return "userlogin";
}

@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password,Model model,HttpServletRequest request) {
User user1 = new User();
user1.setName(username);
user1.setPassword(password);
boolean flag = userDao.login(user1);
if (!flag) {
model.addAttribute("error", "用户名或密码错误");
System.out.println("用户名或密码错误");
// 页面路径
// return "/WEB-INF/view/user/add.jsp";
return "userlogin";
}
// model.addAttribute("username", username);
//将用户信息放入 session 中
request.getSession(true).setAttribute("username",username);
System.out.println("登录成功"+ username);

return "redirect:/users/list";
}

@GetMapping("/logout")
public String logout(HttpSession session,SessionStatus sessionStatus) {
session.removeAttribute("username");
sessionStatus.setComplete();
System.out.println("注销成功");
System.out.println(session.getAttribute("username"));
return "redirect:userlogin";
}
}
  • Controller层的UserController 主要用来处理登录后对用户进行增删改查
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

@Controller
@RequestMapping("/users")
public class UserController {
@Autowired
private UserDao userDao;


@GetMapping("/list")
public String list(Model model) {

List<User> users = userDao.findAll();
model.addAttribute("users", users);
return "userList";
}
@GetMapping("/addPage")
public String addPage(Model model) {
return "useradd";
}
@PostMapping("/add")
public String add(@RequestParam String username, @RequestParam String password, Model model) {
User user = new User();
user.setName(username);
user.setPassword(password);
userDao.save(user);
System.out.println("添加用户信息成功");
return "redirect:/users/list";
}
@GetMapping("/findPage")
public String findPage(@RequestParam Integer id ,Model model) {
User user = userDao.findById(id);
if (user!=null) {
model.addAttribute("user", user);
} else {
System.out.println("用户信息不存在");
}
return "useredit";
}
@PostMapping("/update")
public String update(@RequestParam Integer id ,@RequestParam String username, @RequestParam String password) {
User user = new User();
user.setId(id);
user.setPassword(password);
user.setName(username);
userDao.update(user);
System.out.println("更新成功");
return "redirect:/users/list";
}
@GetMapping("/del")
public String del(@RequestParam Integer id) {
userDao.delete(id);
System.out.println("删除成功");
return "redirect:/users/list";

}
  • LoginInterceptor 主要是对没有登录的进行拦截,自动跳转到登录界面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package xyz.shi.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session = request.getSession();
// 检查用户是否已登录,如果未登录则跳转到登录页
if (session.getAttribute("username") == null) {
response.sendRedirect(request.getContextPath() + "/home/userlogin");
return false;
} else {
return true;
}
}
}
  • jsp界面,路径在/WEB-INF/jsp/目录下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h1>登录</h1>
<form action="${pageContext.request.contextPath}/home/login" method="post">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required><br><br>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required><br><br>
<input type="submit" value="登录">
</form>
</body>
</html>
  • 本地安装tomcat,我的版本为9.0.83,然后idea中装插件Smart tomcat,然后在webapp上右键运行

  • 测试页面

image-20240219174029931

说明

hibernate

一对一

  • 配置基本不变
  • 实体类,一个人对应一个信用卡
1
2
3
4
5
6
7
8
9
10
package xyz.shi.domain;
import javax.persistence.*;
@Entity
@Table(name = "t_idcard")
public class IdCard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String cardNo;
.....
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ckage xyz.shi.domain;

import javax.persistence.*;

@Entity
@Table(name = "t_person")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
// 单向关联只要向子表加入关系
@OneToOne
@JoinColumn(name = "t_idCard_id")
private IdCard idCard;
...

只做了单向关联

  • dao
1
2
3
4
5
6
7
8
9
10
11
12
13
package xyz.shi.dao;

import xyz.shi.domain.QueryResult;
import xyz.shi.domain.Person;

public interface PersonDao {
void save(Person person);
void update(Person person);
Person findById(int id);
QueryResult findAll(int firstResult, int maxResults);
void delete(Person person);
}

  • 实现dao
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
package xyz.shi.dao.Impl;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import xyz.shi.dao.PersonDao;
import xyz.shi.domain.Person;
import xyz.shi.domain.QueryResult;
import xyz.shi.domain.User;
import java.util.List;

@Repository
public class PersonDaoImpl implements PersonDao {
@Autowired
private SessionFactory sessionFactory;
@Override
public void save(Person person) {
Session session = sessionFactory.getCurrentSession();
session.save(person);

}

@Override
public void update(Person person) {
Session session = sessionFactory.getCurrentSession();
session.update(person);
}

@Override
public Person findById(int id) {
Session session = sessionFactory.getCurrentSession();
Person person = session.get(Person.class, id);
return person;
}
@Override
public QueryResult findAll(int firstResult, int maxResults) {
Session session = sessionFactory.getCurrentSession();

List<User> list = session.createQuery(
"FROM Person")
.setFirstResult(firstResult)
.setMaxResults(maxResults)
.list();
Long count = (Long)session.createQuery( //
"SELECT COUNT(*) FROM User") //
.uniqueResult();
return new QueryResult(count.intValue(), list);
}

@Override
public void delete(Person person) {
Session session = sessionFactory.getCurrentSession();
session.delete(person);
}
}

  • 测试
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
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import xyz.shi.dao.PersonDao;
import xyz.shi.domain.IdCard;
import xyz.shi.domain.Person;
import xyz.shi.domain.QueryResult;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class PersonTest {
@Autowired
private PersonDao dao;

@Test
@Transactional
// @Rollback(false) // 不需要回滚
public void save() {
IdCard idCard = new IdCard();
idCard.setId(17);
Person person = new Person();
person.setName("王大伟7");
person.setIdCard(idCard);
dao.save(person);
}
@Test
@Transactional
// @Rollback(false)
public void find() {
QueryResult result = dao.findAll(0, 5);
System.out.println(result.getCount());
for (Object o : result.getList()) {
Person person = (Person) o;
System.out.println(person.getName());
System.out.println(person.getIdCard().getCardNo());
}

}
@Test
@Transactional
@Rollback(false)
public void update() {
IdCard idCard = new IdCard();
idCard.setId(17);
Person person = new Person();
person.setName("王大伟16");
person.setIdCard(idCard);
person.setId(16);
dao.update(person);
}
}

一对多

  • 准备两个实体类,一个班级下有多个学生
  • 班级实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package xyz.shi.domain;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "grade")
public class Grade {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer Id;
private String name;
//持有 Student 引用的集合,来维护一对多关联关系
@OneToMany(mappedBy = "grade", cascade = CascadeType.ALL)
private Set<Student> students = new HashSet<>();
...
  • 学生实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
@Table(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
//持有实体类 Grade 的一个引用,维护多对一关系
// 多对一关联关系
@ManyToOne
@JoinColumn(name = "grade")
private Grade grade;
....
  • dao
1
2
3
4
public interface StudentDao {
void save(Student student);
QueryResult findAll(int firstResult, int maxResults);
}
  • 实现
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

@Repository
public class StudentDaoImpl implements StudentDao {
@Autowired
private SessionFactory sessionFactory;
@Override
public void save(Student student) {
Session session = sessionFactory.getCurrentSession();
session.save(student);
}

@Override
public QueryResult findAll(int firstResult, int maxResults) {
Session session = sessionFactory.getCurrentSession();
List<Object[]> list = session.createQuery( //
"select s, g from Student s join s.grade g") //
.setFirstResult(firstResult) //
.setMaxResults(maxResults) //
.list();

// 查询总记录数
Long count = (Long) session.createQuery( //
"SELECT COUNT(*) FROM Student") //
.uniqueResult();
return new QueryResult(count.intValue(), list);
}
}
  • 测试
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

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class StudentTest {
@Autowired
private StudentDao studentDao;

@Test
@Transactional
// @Rollback(false) // 不需要回滚
public void save() {
Grade grade = new Grade();
grade.setId(36);

Student student = new Student();
student.setName("王大伟36");
//设置学生的班级
student.setGrade(grade);

Student student2 = new Student();
student2.setGrade(grade);
student2.setName("小红36");

studentDao.save(student);
studentDao.save(student2);
}
@Test
@Transactional
public void findAll() {
QueryResult result = studentDao.findAll(0, 5);
System.out.println(result.getCount());
List<Object[]> list = result.getList();
for (Object[] arr : list) {
Student student = (Student) arr[0];
System.out.println("Student: " + student.getName() + ", Grade: " + student.getGrade().getName());

}
}

多对多

  • 多个学生和多个课程
  • 学生实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Entity
@Table(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
//持有实体类 Grade 的一个引用,维护多对一关系
// 多对一关联关系
@ManyToOne
@JoinColumn(name = "gid")
private Grade grade;
//将 Course 对象的集合作为其属性,以维护它们之间的多对多关联关系
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();

@JoinColumn(name = “gid”) 这里的name对应数据表中的字段,若填的字段在数据表中不存在,就会自动创建

  • 课程类
1
2
3
4
5
6
7
8
9
10
@Entity
@Table(name = "course")
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
//学生 Student 的集合作为其属性,维护多对多关联关系
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();

​ @ManyToMany(mappedBy = “courses”) 这里的courses对应Student实体类中的courses

  • dao
1
2
3
4
public interface StudentManyToManyDao {
void save(Student student);
QueryResult findAll(int firstResult, int maxResults);
}
  • 实现dao
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

@Repository
public class StudentManyToManyDaoImpl implements StudentManyToManyDao {
@Autowired
private SessionFactory sessionFactory;
@Override
public void save(Student student) {
Session session = sessionFactory.getCurrentSession();
session.save(student);
}

@Override
public QueryResult findAll(int firstResult, int maxResults) {
Session session = sessionFactory.getCurrentSession();
String hql = "SELECT s, c, g FROM Student s " +
"JOIN s.courses c " +
"JOIN s.grade g";
List<Object[]> list = session.createQuery(hql)
.setFirstResult(firstResult)
.setMaxResults(maxResults)
.list();

// 查询总记录数
Long count = (Long) session.createQuery( //
"SELECT COUNT(*) FROM Student") //
.uniqueResult();
return new QueryResult(count.intValue(), list);
}
  • 测试
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

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class StudentManyToManyTest {
@Autowired
private StudentManyToManyDao studentDao;
@Test
@Transactional
@Rollback(false) // 不需要回滚
public void save() {
//-----新增两个学生,关联到一个班级下面,同时新增的两个学生关联不同的课程(需要用到第三方表)

//一个班级
Grade grade = new Grade();
grade.setId(36);
// 新建两个学生
Student student1 = new Student();
student1.setName("王大伟36121");
//设置学生的班级
student1.setGrade(grade);

Student student2 = new Student();
student2.setGrade(grade);
student2.setName("小红36121");
// 两个学生各自对应两个课程
Course course1 = new Course();
course1.setId(17);

Course course2 = new Course();
course2.setId(18);


studentDao.save(student1);
studentDao.save(student2);

//学生选课的关系相互关联
course1.getStudents().add(student1);
student1.getCourses().add(course1);

course2.getStudents().add(student2);
student2.getCourses().add(course2);
}
@Test
@Transactional
public void findAll() {
QueryResult qresult = studentDao.findAll(0, 5);
System.out.println(qresult.getCount());
List<Object[]> list = qresult.getList();
for (Object[] result : list) {
Student student = (Student) result[0];
Course course = (Course) result[1];
Grade grade = (Grade) result[2];
System.out.println(student);
System.out.println(course);
System.out.println(grade);
}
}
}

Jpa

  • 配置不变

一对一

  • 一个人对应一个信用卡号
  • Person实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package xyz.shi.domain;
import javax.persistence.*;
@Entity
@Table(name = "t_person")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

private String name;
// 单向关联只要向子表加入关系
@OneToOne
// @OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name = "t_idCard_id")
private IdCard idCard;
  • IdCard实体类
1
2
3
4
5
6
7
8
import javax.persistence.*;
@Entity
@Table(name = "t_idcard")
public class IdCard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String cardNo;
  • personDao
1
2
3
4
5
6
7
package xyz.shi.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import xyz.shi.domain.Person;
public interface PersonDao extends JpaRepository<Person, Integer>, JpaSpecificationExecutor<Person> {
}

  • 测试
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
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import xyz.shi.dao.PersonDao;
import xyz.shi.domain.IdCard;
import xyz.shi.domain.Person;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class PersonTest {
@Autowired
PersonDao dao;
@Test
public void testFindPage() {
PageRequest page = PageRequest.of(0, 5, Sort.by(Sort.Order.desc("id")));
Page<Person> all = dao.findAll(page);
System.out.println("分页 + 根据id逆序 查询结果: " + all.getContent());
for(Person person: all) {
System.out.println(person.getName());
System.out.println(person.getIdCard().getCardNo());
}
}
@Test
public void testFindBy() {
Person person = dao.findById(37).get();
System.out.println(person.getName() + "_"+ person.getIdCard().getCardNo());
}
@Test
public void save() {
IdCard idCard = new IdCard();
idCard.setId(14);
Person person = new Person();
person.setName("tttt");
person.setIdCard(idCard);
dao.save(person);
}
@Test
public void update() {
IdCard idCard = new IdCard();
idCard.setId(17);
Person person = new Person();
person.setName("tttt2");
person.setIdCard(idCard);
person.setId(38);
dao.save(person);
}
@Test
public void delete() {
dao.deleteById(37);
}
}

一对多 多对多

  • 一个班级下有多个学生
  • 班级实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package xyz.shi.domain;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "grade")
public class Grade {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer Id;
private String name;
//持有 Student 引用的集合,来维护一对多关联关系
@OneToMany(mappedBy = "grade", cascade = CascadeType.ALL)
private Set<Student> students = new HashSet<>();
...
  • 学生实体类
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
package xyz.shi.domain;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
//持有实体类 Grade 的一个引用,维护多对一关系
// 多对一关联关系
@ManyToOne
@JoinColumn(name = "gid")
private Grade grade;
//将 Course 对象的集合作为其属性,以维护它们之间的多对多关联关系
@ManyToMany(targetEntity = Course.class)
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
....
  • 测试
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
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import xyz.shi.dao.StudentDao;
import xyz.shi.domain.Course;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Student;

import java.util.ArrayList;
import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class StudentTest {
@Autowired
private StudentDao dao;
@Test
public void oneToMany() {
PageRequest page = PageRequest.of(0, 5, Sort.by(Sort.Order.desc("id")));
Page<Student> all = dao.findAll(page);
System.out.println("分页 + 根据id逆序 查询结果: " + all.getContent());
for(Student student: all) {
System.out.println(student.getName());
System.out.println(student.getGrade().getName());
}
Grade grade = new Grade();
grade.setId(36);

Student student = new Student();
student.setName("王大伟4");
//设置学生的班级
student.setGrade(grade);

Student student2 = new Student();
student2.setGrade(grade);
student2.setName("小红5");

List<Student> studentList = new ArrayList<>();
studentList.add(student);
studentList.add(student2);

dao.saveAll(studentList);
// dao.deleteAll();
}
@Test
public void ManyToManyFind() {
Pageable pageable = PageRequest.of(0, 5);
Page<Object[]> page = dao.findAllCourse(pageable);

System.out.println("Page " + page.getNumber() + " of " + page.getTotalPages());
System.out.println("Total elements: " + page.getTotalElements());

for(Object[] objects: page.getContent()) {
Student student = (Student) objects[0];
Course course = (Course) objects[1];
Grade grade = (Grade) objects[2];
System.out.println(student.getName());
System.out.println(course.getName());
System.out.println(grade.getName());
}
}
@Test
public void ManyToManySave() {
//-----新增两个学生,关联到一个班级下面,同时新增的两个学生关联不同的课程(需要用到第三方表)

//一个班级
Grade grade = new Grade();
grade.setId(36);
// 新建两个学生
Student student1 = new Student();
student1.setName("王大伟7");
//设置学生的班级
student1.setGrade(grade);

Student student2 = new Student();
student2.setGrade(grade);
student2.setName("小红71");
// 两个学生各自对应两个课程
Course course1 = new Course();
course1.setId(17);

Course course2 = new Course();
course2.setId(18);

//学生选课的关系相互关联
course1.getStudents().add(student1);
student1.getCourses().add(course1);

course2.getStudents().add(student2);
student2.getCourses().add(course2);

dao.save(student1);
dao.save(student2);
}
@Test
public void ManyToManyUpdate() {
//一个班级
Grade grade = new Grade();
grade.setId(36);


// 新建两个学生
Student student1 = new Student();
student1.setName("王大伟7");
student1.setId(43);
//设置学生的班级
student1.setGrade(grade);

Student student2 = new Student();
student2.setGrade(grade);
student2.setName("小红71");
student2.setId(45);
// 两个学生各自对应两个课程
Course course1 = new Course();
course1.setId(17);

Course course2 = new Course();
course2.setId(18);

//学生选课的关系相互关联

// 先移除关联关系
student1.getCourses().remove(course1);
course1.getStudents().remove(student1);

student2.getCourses().remove(course2);
course2.getStudents().remove(student2);
// 更新关联关系
course1.getStudents().add(student1);
student1.getCourses().add(course1);

course2.getStudents().add(student2);
student2.getCourses().add(course2);

dao.save(student1);
dao.save(student2);
}
@Test
public void ManyToManyDel() {

Student student = new Student();
student.setId(46);

Course course = new Course();
course.setId(39);

// 移除关联关系
student.getCourses().remove(course);
course.getStudents().remove(student);

dao.delete(student);

}
}

Mybatis

一对一

  • 实体类就沿用IdCard和Person

  • 配置需要加入对应的mapper文件

  • mybatis.xml

1
2
3
4
5
6
!-- sql映射文件的位置 -->
<mappers>
<!-- 注册userMapper.xml文件 -->
<mapper resource="mapper/UserMapper.xml"/>
<mapper resource="mapper/PersonMapper.xml"/>
</mappers>
  • application.xml
1
2
3
4
5
6
7
8
9
<!--下面的就是自己定义的service-->
<!--声明service-->
<bean id="userService" class="xyz.shi.service.Impl.UserServiceImpl">
<!--就是上面通过创建的dao对象-->
<property name="userDao" ref="userDao"/>
</bean>
<bean id="personService" class="xyz.shi.service.Impl.PersonServiceImpl">
<property name="personDao" ref="personDao"/>
</bean>
  • PersonMapper.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
<?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 namespace="xyz.shi.dao.PersonDao">
<!-- 定义封装person和IdCard的resultMap -->
<resultMap id="personIdCardMap" type="xyz.shi.entity.Person">
<id property="id" column="id"/>
<!--IdCard的id -->
<result property="name" column="name"/>
<!-- association配置一对一关系,property为Person中属性名,javaType为属性类型 -->
<association property="idCard" javaType="xyz.shi.entity.IdCard">
<!-- column="t_idCard_id 对应person表中的t_idCard_id-->
<id property="id" column="t_idCard_id"/>
<result property="cardNo" column="cardNo"/>
</association>
</resultMap>
<!--配置查询所有-->
<select id="findAll" resultMap="personIdCardMap">
select p.name, p.id as pid, c.id as cid, c.cardNo from t_person p, t_idCard c where c.id = p.t_idCard_id
</select>
<!-- 通过id(唯一)获取用户-->
<select id="queryOne" parameterType="int" resultMap="personIdCardMap">
select p.name, p.id as pid, c.id as cid, c.cardNo from t_person p, t_idCard c where c.id = p.t_idCard_id and p.id=#{id}

</select>
<!-- 插入 -->
<insert id="insert" parameterType="Person">
insert into t_person (name,t_idCard_id) values(#{name},#{idCard.id})
</insert>

<update id="modify" parameterType="Person">
update t_person set name=#{name},t_idCard_id=#{idCard.id} where id=#{id}
</update>
<!-- 根据 id 删除 -->
<delete id="delete" parameterType="int">
delete from t_person where id=#{id}
</delete>
</mapper>

association配置一对一关系,其实就是把之前练习的学习Mybatis的内容移过来

  • dao
1
2
3
4
5
6
7
public interface PersonDao {
Page<Person> findAll();
Person queryOne(int id);
int insert(Person person);
int modify(Person person);
int delete(int id);
}
  • service
1
2
3
4
5
6
7
public interface PersonService {
Page<Person> findAll();
Person queryOne(int id);
int insert(Person person);
int modify(Person person);
int delete(int id);
}
  • 实现service
1
2
3
4
5
6
7
8
9
10
11
12
13

public class PersonServiceImpl implements PersonService {
// 引用类型
private PersonDao personDao;
// 为了使用set注入来赋值,一定要加这个
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
@Override
public Page<Person> findAll() {
return personDao.findAll();
....
}
  • 测试
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
public class PersonTest {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
// 其实就是调用的app..xml中的<bean id="userService" class="xyz.shi.service.Impl.UserServiceImpl">
PersonService personService = (PersonService) context.getBean("personService");

@Test
public void TestFindAll() {
//在查询之前,设置分页条件 显示第一页,展示3条数据
Page<Object> page = PageHelper.startPage(1, 3);
List<Person> personList = personService.findAll();
// 设置分页导航数量5
PageInfo<Person> pageInfo = new PageInfo<>(personList, 5);
for (Person person:personList) {
System.out.println("------每个用户的信息-------");
System.out.println(person.getName());
System.out.println(person.getIdCard().getCardNo());
}
System.out.println();
///xxx
System.out.println("---------全面配置信息:--------");
System.out.println("总数据量 :"+pageInfo.getTotal());
System.out.println("每页的数量:"+pageInfo.getPageSize());
System.out.println("当前页码:"+pageInfo.getPageNum());
System.out.println("是否有上一页:"+pageInfo.isHasPreviousPage());
System.out.println("上一页是:"+pageInfo.getPrePage());
System.out.println("是否有下一页:"+pageInfo.isHasNextPage());
System.out.println("下一页是:"+pageInfo.getNextPage());
System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
System.out.println("导航页的第一个页码是:"+pageInfo.getNavigateFirstPage());
System.out.println("导航页的最后一个页码是:"+pageInfo.getNavigateLastPage());
System.out.println("每页显示的页码个数:"+pageInfo.getNavigatePages());
System.out.println("页码数:"+ Arrays.toString(pageInfo.getNavigatepageNums()));
}
@Test
public void TestQueryOne() {
Person person = personService.queryOne(16);
System.out.println(person.getName());
System.out.println(person.getIdCard().getCardNo());
}
@Test
public void TestAddPerson() {
Person person = new Person();
person.setName("山大王");

IdCard idCard = new IdCard();
idCard.setCardNo("222");
idCard.setId(14);

person.setIdCard(idCard);

personService.insert(person);
}

@Test
public void TestModifyPerson() {
Person person = new Person();
person.setName("山大王234");
person.setId(31);

IdCard idCard = new IdCard();
idCard.setId(17);

person.setIdCard(idCard);
personService.modify(person);
}

@Test
public void TestDelete() {
personService.delete(31);
}
}
  • 一对多和多对多,配置基本一样唯一区别就是在mapper.xml使用association进行关联,他们的测试类分别为:GradeTest(一对多),StudentTest(多对多)

  • 源代码

MybatisPlus

  • mybatis-plus在连表查询上是不行的,如果需要连表查询,那么我们就得乖乖的去写xml文件了
  • 推荐用一个插件,官网在这里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class test {
@Resource
private UserMapper userMapper;

@Test
void testResultMap() {
MPJLambdaWrapper<UserDO> wrapper = new MPJLambdaWrapper<>(User.class)
.selectAll(UserDO.class)
//对多查询
.selectCollection(AddressDO.class, UesrDTO::getAddressList)
//对一查询
.selectAssociation(AddressDO.class, UesrDTO::getAddress)
.leftJoin(AddressDO.class, AddressDO::getUserId, UserDO::getId);

List<UserDTO> dtoList = userMapper.selectJoinList(UserDTO.class, wrapper);

//关于对多分页查询
//由于嵌套结果方式会导致结果集被折叠,因此分页查询的结果在折叠后总数会减少,所以无法保证分页结果数量正确。
}
}

说明

  • 上篇文章主要介绍spring如何整合jdbc、mybatis,本文章继续整合orm中的常用框架

  • 环境:idea社区版本、jdk1.8,mysql8.0+,maven,win10

整合hibernate

  • 依赖文件
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
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<!-- 事务 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.0.7.Final</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.1</version>
</dependency>
</dependencies>
  • src-main-resources-db.properties
1
2
3
4
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai
user=root
password=123456
  • src-main-resources-applicationContext.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
<?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:tx="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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
">
<!-- 引入db.properties文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 创建druid 的数据源 -->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="url" value="${url}" />
<property name="driverClassName" value="${driver}" />
<property name="username" value="${user}" />
<property name="password" value="${password}" />
</bean>
<!-- 配置Hibernate的SessionFactory对象 -->
<bean class="org.springframework.orm.hibernate5.LocalSessionFactoryBean" id="sessionFactory">
<!-- 关联数据源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 配置Hibernate的属性信息 -->
<property name="hibernateProperties">
<props>
<prop key="show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<!-- 扫描路径 -->
<property name="packagesToScan">
<list>
<value>xyz.shi.domain</value>
</list>
</property>
</bean>
<!-- 配置HibernateTemplate对象 -->
<bean class="org.springframework.orm.hibernate5.HibernateTemplate" id="hibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 配置事务管理-->
<bean class="org.springframework.orm.hibernate5.HibernateTransactionManager" id="transactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- 配置扫描路径 -->
<context:component-scan base-package="xyz.shi" />
</beans>
  • userdao
1
2
3
4
5
6
7
8
9
10
11
12
13
package xyz.shi.dao;

import xyz.shi.domain.QueryResult;
import xyz.shi.domain.User;

public interface UserDao {
void save(User user);
void update(User user);
User findById(int id);
QueryResult findAll(int firstResult, int maxResults);
void delete(User user);
}

  • 实现userdao
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
package xyz.shi.dao.Impl;

import org.hibernate.SessionFactory;
import xyz.shi.dao.UserDao;
import xyz.shi.domain.QueryResult;
import xyz.shi.domain.User;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private SessionFactory sessionFactory;
// private HibernateTemplate template;
@Override
public void save(User user) {
Session session = sessionFactory.getCurrentSession();
session.save(user);

}

@Override
public void update(User user) {
Session session = sessionFactory.getCurrentSession();
session.update(user);

}

@Override
public User findById(int id) {
Session session = sessionFactory.getCurrentSession();
User user = session.get(User.class, id);
return user;
}


/**
* 分页的查询数据列表
* @param firstResult 从结果列表中的哪个索引开始取数据
* @param maxResults 最多取多少条数据
* @return 一页的数据列表
*/
@Override
public QueryResult findAll(int firstResult, int maxResults) {
Session session = sessionFactory.getCurrentSession();

List<User> list = session.createQuery(
"FROM User")
.setFirstResult(firstResult)
.setMaxResults(maxResults)
.list();
Long count = (Long)session.createQuery(
"SELECT COUNT(*) FROM User")
.uniqueResult();
return new QueryResult(count.intValue(), list);
}

@Override
public void delete(User user) {
Session session = sessionFactory.getCurrentSession();
session.delete(user);

}
}

  • 分页实体类
1
2
3
4
5
public class QueryResult {

private int count; // 总记录数
private List list; // 一页的数据
...
  • 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
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
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import xyz.shi.dao.UserDao;
import xyz.shi.domain.QueryResult;
import xyz.shi.domain.User;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserTest {
@Autowired
private UserDao dao;

@Test
@Transactional
@Rollback(false) // 不需要回滚
public void save() {
User user = new User();
user.setUserName("test789");
user.setPassword("777777");
dao.save(user);
}
@Test
@Transactional
// 如果不加这个,以上就是回滚,这样的好处就是在单元测试中能测试通过,但是最终不修改数据库的值
@Rollback(false)
public void update() {
User user = new User();
user.setUserId(52);
user.setUserName("test8888");
user.setPassword("888888");
dao.update(user);
}
@Test
@Transactional
public void findId() {
User user = dao.findById(52);
System.out.println(user.getUserName());
}
@Test
@Transactional
public void findAll() {
QueryResult result = dao.findAll(0, 5);
System.out.println(result.getCount());
for (Object o : result.getList()) {
User user = (User)o;
System.out.println(user.getUserName());
}
}
@Test
@Transactional
public void delete() {
User user = new User();
user.setUserId(53);
dao.delete(user);
}
}

注意这里的标识

Transactional 开启事务

Rollback(false) 这样的好处就是在单元测试中能测试通过,但是最终不修改数据库的值,在发布到生产环境需要检查,切记需要去掉此标识,不然造成修改数据全部都回滚了,造成数据修改不成功

jpa

  • jpa是基于hibernate来实现的

  • 依赖文件

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
    <dependencies>
<!-- Spring框架核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.4</version>
</dependency>
<!-- Spring JPA依赖 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.5.4</version>
</dependency>
<!-- Hibernate JPA实现依赖 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.27.Final</version>
</dependency>

<!-- MySQL连接驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
  • 实体类,映射数据库中表的关系
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
package xyz.shi.domain;

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name="users")
public class User implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private Integer userId;
@Column(name="name")
private String userName;
@Column(name="password")
private String password;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}

}
  • src-main-java-resources-applicationContext.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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

<context:annotation-config />

<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
<value>xyz.shi.domain</value> <!-- 替换成你的实体类所在包名 -->
</list>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</prop> <!-- 替换成你所使用的MySQL版本对应的方言类 -->
<prop key="hibernate.hbm2ddl.auto">update</prop> <!-- 自动更新表结构 -->
</props>
</property>
</bean>

<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" /> <!-- 替换成你所使用的MySQL版本对应的驱动类 -->
<property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai" /> <!-- 替换成你的数据库连接信息 -->
<property name="username" value="root" /> <!-- 替换成你的数据库用户名 -->
<property name="password" value="123456" /> <!-- 替换成你的数据库密码 -->
</bean>

<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<jpa:repositories base-package="xyz.shi.dao" /> <!-- 替换成你的Repository接口所在包名 -->

</beans>

  • dao层,几乎不用写任何逻辑,当然页支持自定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package xyz.shi.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import xyz.shi.domain.User;



public interface UserDao extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> {
// 普通的查询方法
// 示例 getByName(String name):getBy固定用法
//1. Name是User成员变量的首字母大写
//2. name与User的成员变量name相同
// public User getByUserName(String name);
// @Query("SELECT u FROM User u WHERE u.name LIKE %:name%")
// List<User> findByNameContaining(@Param("name") String name);
}

  • 测试src-test-java-UserTest1.class
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
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import xyz.shi.dao.UserDao;
import xyz.shi.domain.User;

import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")

public class UserTest1 {
@Resource
UserDao userDao;
@Test
public void testFindPage() {

PageRequest page = PageRequest.of(0, 5, Sort.by(Sort.Order.desc("userId")));
Page<User> all = userDao.findAll(page);
System.out.println("分页 + 根据id逆序 查询结果: " + all.getContent());
}
@Test
public void testFindBy() {
User user2 = userDao.findById(50).get();
System.out.println(user2.getUserName());
}
@Test
public void save() {
User user = new User();
user.setUserName("tttt");
user.setPassword("pppp");
User user11 = userDao.save(user);
System.out.println(user11.getUserId());
}
@Test
public void update() {
User user = new User();
user.setUserName("tttt60");
user.setPassword("pppp60");
user.setUserId(60);
userDao.save(user);
}
@Test
public void delete() {
User user = new User();
user.setUserId(59);
userDao.delete(user);
}
}

mybatisplus

  • 这篇文章练习了spring集成mybatis,本次用mybatisplus

  • java8,mysql8,idea社区版,win10

  • 依赖文件

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
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.4</version>
</dependency>

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
</dependencies>
  • 实体类,User
1
2
3
4
5
6
7
8
9
10
11
12
13
package xyz.shi.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("users")

public class User {
@TableId
private int id;
private String name;
private String password;
}
  • UserMapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package xyz.shi.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import xyz.shi.entity.User;

@Mapper
// BaseMapper中包含了常用的增删改查的方法,也可以自定义方法
public interface UserMapper extends BaseMapper<User> {
// @Select("SELECT * FROM users WHERE id = #{id}")
// User getUserById(int id);
//
// @Insert("INSERT INTO users(name,) VALUES(#{name}})")
// int insertUser(User user);
//
//// @Update("UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}")
// @Update("UPDATE user SET name = #{name} WHERE id = #{id}")
// int updateUser(User user);
//
// @Select("SELECT * FROM users WHERE name = #{name}")
// List<User> getUsersByName(String name);
}
  • src-main-resources-appcationContext.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
<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="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&amp;serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="autoCommit" value="true"/>
<property name="connectionTimeout" value="5000" />
<property name="idleTimeout" value="60" />
</bean>
<!--jdbc的xml配置-->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<constructor-arg name="configuration" ref="hikariConfig"/>
</bean>
<!-- 配置SqlSessionFactory -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!-- 配置MapperScannerConfigurer -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="xyz.shi.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
</beans>
  • src-main-resources-mybatis-config.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--settings:控制mybatis全局行为-->
<settings>
<!--设置mybatis输出日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--设置别名-->
<typeAliases>
<!--
package:把包下面的所有类名作为别名
name:实体类所在的包名-->
<package name="xyz.shi.entity"/>
</typeAliases>
</configuration>
  • 单元测试,src-test-java-UserMapper
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
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import xyz.shi.entity.User;
import xyz.shi.mapper.UserMapper;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserMapperTest {
@Autowired
UserMapper userDao;
@Test
public void selectById() {
User user = userDao.selectById(50);
System.out.println(user.getName());
}
@Test
public void findPage() {
Page<User> page = new Page<>(1, 10);
List<User> userList = userDao.selectPage(page, null).getRecords();
for(User user1: userList) {
System.out.println(user1.getName());
}
}
@Test
public void save() {
User user = new User();
user.setName("mybatisplus");
user.setPassword("11111");
userDao.insert(user);
}
@Test
public void update() {
User user = new User();
user.setId(61);
user.setName("tst60");
user.setPassword("2222");
userDao.updateById(user);
}
@Test
public void delete() {
userDao.deleteById(61);
}
}

  • 注意本次没有用mapper.xml文件,完全抛弃了
  • 源代码

说明

  • 本地环境:mysql 8.0,java 1.8,idea社区版本
  • 这篇文章 介绍了对Hibernate的关系映射的使用
  • 之前写过jsp+mybatis的文章,本次详细介绍mybatis的常见用法

依赖文件

  • pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>

一对一

  • 这篇文章 介绍了对Hibernate的关系映射

  • 直接用表t_idCard、t_person

  • 直接用Person、IdCard实体类

  • 创建db.properties文件,在resources目录下

1
2
3
4
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai
user=root
password=123456
  • 接下来就要创建一个xml文件作为MyBatis的核心配置文件了,src-main-resources-mybatis-config.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
<!--mybatis-config.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

<!--读取配置文件中的数据库连接信息-->
<properties resource="db.properties"/>
<settings>
<!--设置日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--开启驼峰命名,映射数据库到对象属性名-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>

<!--给实体类取别名,为了后面配置mapper.xml时不用写全限定名,方便使用-->
<typeAliases>
<!--包扫描-->
<package name="xyz.shi.entity"/>
</typeAliases>

<environments default="development">
<!--id属性必须和上面的default一样 -->
<environment id="development">
<transactionManager type="JDBC"/>
<!--dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象源 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 注册userMapper.xml文件 -->
<mapper resource="UserMapper.xml"/>
</mappers>

</configuration>
  • src-main-resources-personMapper.xml 新建如下person表的增删改查
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
<?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 namespace="personMapper">
<!-- 定义封装person和IdCard的resultMap -->
<resultMap id="personIdCardMap" type="xyz.shi.domain.Person">
<id property="id" column="id"/>
<!--IdCard的id -->
<!-- <result property="t_idCard_id" column="t_idCard_id"/>-->
<result property="name" column="name"/>
<!-- association配置一对一关系,property为Person中属性名,javaType为属性类型 -->
<association property="idCard" javaType="xyz.shi.domain.IdCard">
<!-- column="t_idCard_id 对应person表中的t_idCard_id-->
<id property="id" column="t_idCard_id"/>
<result property="cardNo" column="cardNo"/>
</association>
</resultMap>
<!--配置查询所有-->
<select id="findAll" resultMap="personIdCardMap">
select p.name, p.id as pid, c.id as cid, c.cardNo from t_person p, t_idCard c where c.id = p.t_idCard_id
</select>
<!-- 通过id(唯一)获取用户-->
<select id="queryOne" parameterType="int" resultMap="personIdCardMap">
select p.name, p.id as pid, c.id as cid, c.cardNo from t_person p, t_idCard c where c.id = p.t_idCard_id and p.id=#{id}

</select>
<!-- 插入 -->
<insert id="insertPerson" parameterType="Person">
insert into t_person (name,t_idCard_id) values(#{name},#{idCard.id})
</insert>

<update id="modifyPerson" parameterType="Person">
update t_person set name=#{name},t_idCard_id=#{idCard.id} where id=#{id}
</update>
<!-- 根据 id 删除 -->
<delete id="deletePerson" parameterType="int">
delete from t_person where id=#{id}
</delete>
</mapper>
  • personDao
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package xyz.shi.dao;

import xyz.shi.domain.Person;

import java.util.List;

public interface PersonDao {
List<Person> findAll();
Person queryOne(int id);
int insert(Person person);
int modify(Person person);
int delete(int id);
}

  • 实现personDao接口
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
package xyz.shi.dao.impl;

import org.apache.ibatis.session.SqlSession;
import xyz.shi.dao.PersonDao;
import xyz.shi.domain.Person;
import xyz.shi.utils.MybatisUtils;
import java.util.List;

public class PersonDaoImpl implements PersonDao {
@Override
public List<Person> findAll() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 命名空间+id,这就是命名空间的作用:便于区分
List<Person> personList = sqlSession.selectList("personMapper.findAll");
sqlSession.close();
return personList;
}

@Override
public Person queryOne(int id) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
Person person = sqlSession.selectOne("personMapper.queryOne", id);
sqlSession.close();
return person;

}

@Override
public int insert(Person person) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.insert("personMapper.insertPerson", person);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int modify(Person person) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.update("personMapper.modifyPerson", person);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int delete(int id) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.delete("personMapper.deletePerson", id);
sqlSession.commit();
sqlSession.close();
return num;
}

}

  • sqlsession代码如下
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
package xyz.shi.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MybatisUtils {

//sqlSession工厂
private static SqlSessionFactory sqlSessionFactory;

//静态代码块,直接项目跑起来直接装载
static {
//设置要读取的资源路径
String resource = "mybatis.config.xml";
//声明一个输入流,方便后面读取
InputStream inputStream = null;

try {
//通过ibatis包下的资源读取的方法,通过提前设定好的路径将mybatis的核心配置文件读取进来,用来建造工厂
inputStream = Resources.getResourceAsStream(resource);
//new一个建造者,通过建造者创建一个带核心配置文件的工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}

//通过工厂打开一个对数据库的会话
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}

  • 测试代码
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
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.junit.Test;
import xyz.shi.dao.PersonDao;
import xyz.shi.dao.impl.PersonDaoImpl;
import xyz.shi.domain.IdCard;
import xyz.shi.domain.Person;

import java.util.Arrays;
import java.util.List;

public class PersonDaoTest {
private PersonDao personDao = new PersonDaoImpl();
@Test
public void TestFindAll() {
//在查询之前,设置分页条件 显示第一页,展示3条数据
Page<Object> page = PageHelper.startPage(1, 3);

List<Person> personList = personDao.findAll();

PageInfo<Person> pageInfo = new PageInfo<>(personList, 5);

for (Person person:personList) {
System.out.println("------每个用户的信息-------");
System.out.println(person.getName());
System.out.println(person.getIdCard().getCardNo());
}
System.out.println();
///xxx
System.out.println("---------全面配置信息:--------");
System.out.println("总数据量 :"+pageInfo.getTotal());
System.out.println("每页的数量:"+pageInfo.getPageSize());
System.out.println("当前页码:"+pageInfo.getPageNum());
System.out.println("是否有上一页:"+pageInfo.isHasPreviousPage());
System.out.println("上一页是:"+pageInfo.getPrePage());
System.out.println("是否有下一页:"+pageInfo.isHasNextPage());
System.out.println("下一页是:"+pageInfo.getNextPage());
System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
System.out.println("导航页的第一个页码是:"+pageInfo.getNavigateFirstPage());
System.out.println("导航页的最后一个页码是:"+pageInfo.getNavigateLastPage());
System.out.println("每页显示的页码个数:"+pageInfo.getNavigatePages());
System.out.println("页码数:"+ Arrays.toString(pageInfo.getNavigatepageNums()));
}
@Test
public void TestQueryOne() {
Person person = personDao.queryOne(16);
System.out.println(person.getName());
System.out.println(person.getIdCard().getCardNo());
}
@Test
public void TestAddPerson() {
Person person = new Person();
person.setName("山大王");

IdCard idCard = new IdCard();
idCard.setCardNo("222");
idCard.setId(14);

person.setIdCard(idCard);

personDao.insert(person);
}

@Test
public void TestModifyPerson() {
Person person = new Person();
person.setName("山大王234");
person.setId(29);

IdCard idCard = new IdCard();
idCard.setId(17);

person.setIdCard(idCard);
personDao.modify(person);
}

@Test
public void TestDelete() {
personDao.delete(24);
}
}

一对多

  • 这篇文章 介绍了对Hibernate的关系映射

  • 直接用表student(多对一)、grade(一对多,一个年级下有多个学生)

  • 直接用Student、Grade实体类

  • src-main-resources-gradeMapper.xml 新建如下grade表的增删改查

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
<?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 namespace="gradeMapper">
<!-- 定义封装resultMap
property 实体中对应的属性
column 数据库对应的列(select查询返回的列表)
-->
<resultMap id="gradeStudentMap" type="xyz.shi.domain.Grade">
<id property="id" column="gid"/>
<result property="name" column="gname"/>
<!--一对多关系的时候,一方使用collection标签映射多方 -->
<collection property="students" ofType="xyz.shi.domain.Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>

</collection>
</resultMap>
<!--配置查询所有,重复的列需要重命名-->
<select id="findAll" resultMap="gradeStudentMap">
select g.id as gid, g.name as gname, s.id as sid, s.name as sname from grade g left join student s on g.id = s.gid
</select>
<!-- 通过id(唯一)获取用户-->
<select id="queryOne" parameterType="int" resultMap="gradeStudentMap">
select s.name as sname,g.name as gname, g.id as gid, s.id as sid from grade g, student s where g.id = s.gid and g.id=#{id}
</select>
<!-- 插入 userGeneratedKeys和keyProperty属性可以让插入后对象获得本身自己的id
-->
<insert id="insertGrade" parameterType="Grade"
useGeneratedKeys="true" keyProperty="id">
insert into grade (name) values(#{name})
</insert>
<insert id="insertStudent" parameterType="xyz.shi.domain.Student">
INSERT INTO student (name, gid) VALUES (#{name}, #{grade.id})
</insert>
<!-- 批量新增的时候,注意接收的对象是一个列表,需要在动态拼接sql的时候遍历参数集合对象 -->
<insert id="insertBatchStudent" parameterType="java.util.List">
Insert into student (name,gid) values
<foreach collection="list" item="item" separator=",">
(#{item.name},#{item.grade.id})
</foreach>
</insert>
<update id="updateBatchStudent" parameterType="java.util.List">
<foreach collection="list" item="item" separator=";">
update student set name = #{item.name},gid = #{item.grade.id} where id = #{item.id}
</foreach>
</update>
<delete id="deleteBatchStudent" parameterType="java.util.List">
delete from student where id in
<foreach collection="list" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
</mapper>

collection 维护一对多

注意foreach的用法

  • src-main-resources-studentMapper.xml 新建如下student表的查询
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
<?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 namespace="studentMapper">
<!-- 定义封装resultMap
property 实体中对应的属性
column 数据库对应的列(select查询返回的列表)
-->
<resultMap type="xyz.shi.domain.Student" id="StudentGradeResult">
<id column="sid" jdbcType="BIGINT" property="id" />
<result column="sname" jdbcType="VARCHAR" property="name" />
<!-- 多对一关系的时候,多方使用association标签映射一方-->
<!-- <association property="grade" resultMap="StudentResult"></association>-->
<association property="grade" javaType="xyz.shi.domain.Grade">
<id column="gid" property="id" />
<result column="gname" property="name" />
</association>

</resultMap>
<select id="queryOne" parameterType="int" resultMap="StudentGradeResult">
select s.name as sname,g.name as gname, g.id as gid, s.id as sid from student s,grade g where g.id = s.gid and s.id=#{id}
</select>
</mapper>

association 维护多对一

  • 编写班级grade的接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package xyz.shi.dao;

import xyz.shi.domain.Grade;
import xyz.shi.domain.Student;

import java.util.List;

public interface GradeDao {
List<Grade> findAll();
Grade queryOne(int id);
int insert(Grade grade);
// 批量插入学生
int insertBatchStudent(List<Student> students);
int updateBatchStudent(List<Student> students);
int deleteBatchStudent(List<Integer> ids);
}
  • 实现grade接口
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
package xyz.shi.dao.impl;

import org.apache.ibatis.session.SqlSession;
import xyz.shi.dao.GradeDao;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Student;
import xyz.shi.utils.MybatisUtils;

import java.util.List;

public class GradeDaoImpl implements GradeDao {
@Override
public List<Grade> findAll() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 命名空间+id,这就是命名空间的作用:便于区分
List<Grade> gradesList = sqlSession.selectList("gradeMapper.findAll");
sqlSession.commit();
sqlSession.close();
return gradesList;
}

@Override
public Grade queryOne(int id) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
Grade grade = sqlSession.selectOne("gradeMapper.queryOne", id);
sqlSession.close();
return grade;
}

@Override
public int insert(Grade grade) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.insert("gradeMapper.insertGrade", grade);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int insertBatchStudent(List<Student> students) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.insert("gradeMapper.insertBatchStudent",students);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int updateBatchStudent(List<Student> students) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.update("gradeMapper.updateBatchStudent",students);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int deleteBatchStudent(List<Integer> ids) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.delete("gradeMapper.deleteBatchStudent",ids);
sqlSession.commit();
sqlSession.close();
return num;
}
}

  • 执行测试
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
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.junit.Test;
import xyz.shi.dao.GradeDao;
import xyz.shi.dao.PersonDao;
import xyz.shi.dao.StudentDao;
import xyz.shi.dao.impl.GradeDaoImpl;
import xyz.shi.dao.impl.PersonDaoImpl;
import xyz.shi.dao.impl.StudentDaoImpl;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Person;
import xyz.shi.domain.Student;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

public class GradeDaoTest {
private GradeDao gradeDao = new GradeDaoImpl();
@Test
public void findAll() {
//在查询之前,设置分页条件 显示第一页,展示10条数据
Page<Object> page = PageHelper.startPage(1, 10);

List<Grade> gradeList = gradeDao.findAll();

PageInfo<Grade> pageInfo = new PageInfo<>(gradeList, 5);

for (Grade grade:gradeList) {
System.out.println("------每个用户的信息-------");
System.out.println(grade.getName());
System.out.println(grade.getStudents());
List<Student> students = grade.getStudents();
for(Student student: students) {
System.out.println(student.getName());

}

}
System.out.println();
///xxx
System.out.println("---------全面配置信息:--------");
System.out.println("总数据量 :"+pageInfo.getTotal());
System.out.println("每页的数量:"+pageInfo.getPageSize());
System.out.println("当前页码:"+pageInfo.getPageNum());
System.out.println("是否有上一页:"+pageInfo.isHasPreviousPage());
System.out.println("上一页是:"+pageInfo.getPrePage());
System.out.println("是否有下一页:"+pageInfo.isHasNextPage());
System.out.println("下一页是:"+pageInfo.getNextPage());
System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
System.out.println("导航页的第一个页码是:"+pageInfo.getNavigateFirstPage());
System.out.println("导航页的最后一个页码是:"+pageInfo.getNavigateLastPage());
System.out.println("每页显示的页码个数:"+pageInfo.getNavigatePages());
System.out.println("页码数:"+ Arrays.toString(pageInfo.getNavigatepageNums()));
}
@Test
public void queryOne() {
Grade grade = gradeDao.queryOne(2);
System.out.println(grade.getName());
List<Student> students = grade.getStudents();
for(Student student: students) {
System.out.println(student.getName());
}
}
@Test
public void queryOneStudent(){
StudentDao studentDao = new StudentDaoImpl();
Student student = studentDao.queryOne(3);
System.out.println(student);
}
@Test
public void insertGrade() {
Grade grade = new Grade();
grade.setName("初中");
gradeDao.insert(grade);
}
@Test
public void insertBatchStudent() {
Grade grade = new Grade();
grade.setName("初二");
gradeDao.insert(grade);

List<Student> students = new ArrayList<>();

Student student1 = new Student();
student1.setName("s1");
student1.setGrade(grade);
students.add(student1);

Student student2 = new Student();
student2.setName("s2");
student2.setGrade(grade);
students.add(student2);

gradeDao.insertBatchStudent(students);
}
@Test
public void updateBatchStudent() {
Grade grade = new Grade();
grade.setId(25);

List<Student> students = new ArrayList<>();
students.add(new Student(41, "新姓名1", grade));
students.add(new Student(42, "新姓名2", grade));
students.add(new Student(43, "新姓名3", grade));

gradeDao.updateBatchStudent(students);
}

@Test
public void deleteBatchStudent() {
List<Integer> ids = new ArrayList<>();
ids.add(3);
ids.add(4);
ids.add(50);
gradeDao.deleteBatchStudent(ids);
// 如果想删除班级和学生呢,可以先删除学生然后再次删除年纪,就是写两个delete语句
//delete from student where gid = #{gid}
//delete from grade where id = #{gid}
}

}

多对多

  • 直接用表student、course(课程表)

  • 直接用Student、course实体类

  • src-main-resources-studentMapper.xml 新建如下student表的增删改查

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
<?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 namespace="studentMapper">
<!-- 定义封装resultMap
property 实体中对应的属性
column 数据库对应的列(select查询返回的列表)
-->
<resultMap type="xyz.shi.domain.Student" id="StudentGradeResult">
<id column="sid" jdbcType="BIGINT" property="id" />
<result column="sname" jdbcType="VARCHAR" property="name" />
<!-- 多对一关系的时候,多方使用association标签映射一方-->
<!-- <association property="grade" resultMap="StudentResult"></association>-->
<association property="grade" javaType="xyz.shi.domain.Grade">
<id column="gid" property="id" />
<result column="gname" property="name" />
</association>
</resultMap>

<resultMap type="xyz.shi.domain.Student" id="StudentCourseResult">
<id column="sid" jdbcType="BIGINT" property="id" />
<result column="sname" jdbcType="VARCHAR" property="name" />
<!-- 一对多 -->
<collection property="courses" ofType="xyz.shi.domain.Course">
<id property="id" column="cid"/>
<result property="name" column="cname"/>

</collection>
</resultMap>
<select id="queryOne" parameterType="int" resultMap="StudentCourseResult">
select s.name as sname,g.name as gname, g.id as gid, s.id as sid from student s,grade g where g.id = s.gid and s.id=#{id}
</select>
<select id="findAllStuCourse" resultMap="StudentCourseResult">
SELECT c.name as cname, c.id as cid, s.id as sid, s.name as sname
FROM student s
INNER JOIN student_course sc ON s.id = sc.student_id
INNER JOIN course c ON sc.course_id = c.id
</select>

<insert id="insertStudent" parameterType="xyz.shi.domain.Student"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO student (name, gid) VALUES (#{name}, #{grade.id})
</insert>
<!---学生关联多个课程 -->
<insert id="batchInsertStudentCourse">
INSERT INTO student_course (student_id, course_id)
VALUES
<foreach collection="courseIds" item="courseId" separator=",">
(#{studentId}, #{courseId})
</foreach>
</insert>
<update id="updateStudent" parameterType="Student">
UPDATE student
SET name = #{name}
WHERE id = #{id}
</update>
<update id="updateStudentCourse" parameterType="java.util.Map">
UPDATE student SET name = #{student.name} WHERE id = #{student.id};
<!-- 先清空关联关系,然后重写插入 -->
DELETE FROM student_course WHERE student_id = #{student.id};
<foreach collection="courseIds" item="courseId" separator=";">
INSERT INTO student_course (student_id, course_id) VALUES (#{student.id}, #{courseId})
</foreach>
</update>
<delete id="deleteStudentAndRelations" parameterType="int">
DELETE FROM student WHERE id = #{id};
DELETE FROM student_course WHERE student_id = #{id};
</delete>
</mapper>
  • studentDao接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package xyz.shi.dao;

import xyz.shi.domain.Course;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Student;

import java.util.List;
import java.util.Map;

public interface StudentDao {
Student queryOne(int id);
List<Student> findAll();
int insert(Student student);
int batchInsertStudentCourse(Map<String, Object> parameterMap);
int updateStudent(Student student);
int updateStudentCourse(Map<String, Object> parameterMap);
int deleteStudentAndRelations(Integer id);
}
  • 实现studentDao接口
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
package xyz.shi.dao.impl;

import org.apache.ibatis.session.SqlSession;
import xyz.shi.dao.StudentDao;
import xyz.shi.domain.Student;
import xyz.shi.utils.MybatisUtils;

import java.util.List;
import java.util.Map;

public class StudentDaoImpl implements StudentDao {

@Override
public Student queryOne(int id) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
Student student = sqlSession.selectOne("studentMapper.queryOne", id);
sqlSession.close();
return student;
}

@Override
public List<Student> findAll() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
List<Student> students = sqlSession.selectList("studentMapper.findAllStuCourse");
sqlSession.commit();
sqlSession.close();
return students;

}

@Override
public int insert(Student student) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.insert("studentMapper.insertStudent", student);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int batchInsertStudentCourse(Map<String, Object> parameterMap) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.insert("studentMapper.batchInsertStudentCourse", parameterMap);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int updateStudent(Student student) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.update("studentMapper.updateStudent", student);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int updateStudentCourse(Map<String, Object> parameterMap) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.update("studentMapper.updateStudentCourse", parameterMap);
sqlSession.commit();
sqlSession.close();
return num;
}

@Override
public int deleteStudentAndRelations(Integer id) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
int num = sqlSession.update("studentMapper.deleteStudentAndRelations", id);
sqlSession.commit();
sqlSession.close();
return num;
}
}

  • 测试
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
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.junit.Test;
import xyz.shi.dao.GradeDao;
import xyz.shi.dao.PersonDao;
import xyz.shi.dao.StudentDao;
import xyz.shi.dao.impl.StudentDaoImpl;
import xyz.shi.domain.Course;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Person;
import xyz.shi.domain.Student;

import java.util.*;

public class StudentDaoTest {
private StudentDao studentDao = new StudentDaoImpl();

@Test
public void findAllStuCourse() {
//在查询之前,设置分页条件 显示第一页,展示10条数据
Page<Object> page = PageHelper.startPage(1, 3);

List<Student> students = studentDao.findAll();

PageInfo<Student> pageInfo = new PageInfo<>(students, 5);

for (Student student:students) {
System.out.println("------每个用户的信息-------");
System.out.println(student);
System.out.println(student.getCourses());
List<Course> courses = student.getCourses();
for(Course course: courses) {
System.out.println(course.getName());
System.out.println(course.getId());
}
}
System.out.println();
///xxx
System.out.println("---------全面配置信息:--------");
System.out.println("总数据量 :"+pageInfo.getTotal());
System.out.println("每页的数量:"+pageInfo.getPageSize());
System.out.println("当前页码:"+pageInfo.getPageNum());
System.out.println("是否有上一页:"+pageInfo.isHasPreviousPage());
System.out.println("上一页是:"+pageInfo.getPrePage());
System.out.println("是否有下一页:"+pageInfo.isHasNextPage());
System.out.println("下一页是:"+pageInfo.getNextPage());
System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
System.out.println("导航页的第一个页码是:"+pageInfo.getNavigateFirstPage());
System.out.println("导航页的最后一个页码是:"+pageInfo.getNavigateLastPage());
System.out.println("每页显示的页码个数:"+pageInfo.getNavigatePages());
System.out.println("页码数:"+ Arrays.toString(pageInfo.getNavigatepageNums()));
}

@Test
public void insertBatchStudent() {
// 设置年纪
Grade grade = new Grade();
grade.setId(2);

Student student1 = new Student();
student1.setName("刘小龙1");
// 关联当前学生的年级
student1.setGrade(grade);
studentDao.insert(student1);

Map<String, Object> parameterMap = new HashMap<>();
parameterMap.put("studentId", student1.getId());
// 手动设置多个科目信息,也可以做批量新增科目
List<Integer> courseIds = Arrays.asList(17, 18);
parameterMap.put("courseIds", courseIds);

studentDao.batchInsertStudentCourse(parameterMap);
}
@Test
public void updateStudentCourse() {
// 要更新的学生信息
Student student = new Student();
student.setId(41);
// 学生新的名字
student.setName("张三");

Map<String, Object> parameterMap = new HashMap<>();
parameterMap.put("student", student);
// 手动设置多个科目信息,也可以做批量新增科目
List<Integer> courseIds = Arrays.asList(29, 30);
parameterMap.put("courseIds", courseIds);
// 更新学生的关联的课程
studentDao.updateStudentCourse(parameterMap);
}
@Test
public void deleteStudentAndRelations() {
studentDao.deleteStudentAndRelations(44);
}
}

总结

  • mapper中resultMap 下用collection为一对多的关联,用association为一对一的关联

  • 一对多或者多对多,更新关系时,先批量删除,然后再批量插入

  • 批量修改数据,需要在db字符串中加上&allowMultiQueries=true

  • 缓存,本次不做演练看目标博客即可

  • 代码在这里

说明

  • 本次主要针对orm中mybatis和Hibernate的关系映射进行实践

  • 本地环境:mysql 8.0,java 1.8,idea社区版本

  • 首先需要阅读这篇文章 介绍了对Hibernate的使用

  • 本次实践关系映射

一对一

  • 一对一的外键字段可以建在任何一张表中,但是,推荐建在查询频率较高的一张表中
  • 准备如下两个表
1
2
3
4
5
6
7
8
9
10
11
12
13

create table t_idCard(
id int primary key auto_increment comment '信用卡id',
cardNo varchar(255) comment '信用卡编号'
);

create table t_person (
id int primary key auto_increment comment '人员的id',
name varchar(32) comment '人员信息',
t_idCard_id int comment '信用卡id',
foreign key(t_idCard_id) references t_idCard(id)
);

foreign key(t_idCard_id) references t_idCard(id) 表示就是t_idCard_id是t_person的外键

  • 新增Person实体类
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
package xyz.shi.domain;

public class Person {

private int id;

private String name;

private IdCard idCard;

public int getId() {
return id;
}

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

public String getName() {
return name;
}

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

public IdCard getIdCard() {
return idCard;
}

public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
}
  • 新增idcard实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package xyz.shi.domain;

public class IdCard {
private int id;
private String cardNo;
public int getId() {
return id;
}

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

public String getCardNo() {
return cardNo;
}

public void setCardNo(String cardNo) {
this.cardNo = cardNo;
}
}

  • 分页的实例类
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
package xyz.shi.entity;
import java.util.List;

public class QueryResult {
private int count; // 总记录数
private List list; // 一页的数据
public void setCount(int count) {
this.count = count;
}
public int getCount() {
return count;
}
public void setList(List list) {
this.list = list;
}
public List getList() {
return list;
}
public QueryResult(int count, List list) {
this.count = count;
this.list = list;
}
@Override
public String toString() {
return "QueryResult{" +
"count=" + count +
", list=" + list +
'}';
}
}


  • 实体 src-man-resources-IdCard.hbm.xml对应的xml配置文件
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="xyz.shi.domain.IdCard" table="t_idCard">
<id name="id">
<generator class="native"/>
</id>
<property name="cardNo"/>
</class>
</hibernate-mapping>
  • 实体 src-man-resources-Persion.hbm.xml对应的xml配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="xyz.shi.domain">
<class name="Person" table="t_person">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"/>
<!-- 一对一配置,设置属性unique=”true”就变成了一对一 -->
<!-- 配置一对一的关系,基于外键的方式 -->
<many-to-one name="idCard" column="t_idCard_id" unique="true" cascade="all" ></many-to-one>
</class>
</hibernate-mapping>

外键有两种关联关系:

1.基于主键的方式,我这里一直失败,Persion外键idCard的值一直为空

1
2
3
4
5
6
7
<!-- 共享主键用此 (一对一关系) -->
<id name="id">
<generator class="foreign">
<param name="property">idCard</param>
</generator>
</id>
<one-to-one name="idCard" class="IdCard" constrained="true"></one-to-one> -

2.基于外键的方式,这里的cascade是级联关联,一定要加上,不然报错

cascade属性:

all :: 所有情况下均进行关联操作。
none: 所有情况下均不进行关联操作。这是默认值。
save-update: 在执行save/update/saveOrUpdate时进行关联操作。
delete: 在执行delete时进行关联操

-

  • 在 Hibernate 核心配置文件 src-man-resources-hibernate.cfg.xml 中,使用 元素来指定映射文件 Student.hbm.xml 和 Grade.hbm.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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 配置关于数据库连接的四个项:driverClass url username password -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>

<!-- 可以将向数据库发送的SQL语句显示出来 -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化SQL语句 -->
<property name="hibernate.format_sql">true</property>
<!-- hibernate的方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 配置hibernate的映射文件所在的位置 -->
<mapping resource="IdCard.hbm.xml"/>
<mapping resource="Person.hbm.xml"/>
</session-factory>
</hibernate-configuration>

  • Dao代码
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
package xyz.shi.dao;

import org.hibernate.Session;
import org.hibernate.Transaction;
import xyz.shi.domain.Person;
import xyz.shi.entity.QueryResult;
import xyz.shi.utils.HibernateUtils;

import java.util.List;

public class PersonDao {
/*
* 保存
*/
public void save(Person person) {
Session session = HibernateUtils.openSession();
try {
Transaction tx = session.beginTransaction(); // 开启事务
session.save(person);
tx.commit(); // 提交事务
} catch (RuntimeException e) {
session.getTransaction().rollback(); // 回滚事务
throw e;
} finally {
session.close(); // 关闭session
}
}

/*
* 更新
*/
public void update(Person person) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

session.update(person);// 操作

tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 删除
*/
public void delete(int id) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

Object user = session.get(Person.class, id); // 要先获取到这个对象
session.delete(user); // 删除的是实体对象

tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 根据id查询一个User数据
*/
public Person getById(int id) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Person person = (Person) session.get(Person.class, id);// 操作
tx.commit();
return person;
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 查询所有
*/
public List<Person> findAll() {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

// 方式一:使用HQL语句
// 这里的FROM User 并不是表的名字,而是User.hbm.xml中的<class name="User" 这个name
List<Person> list = session.createQuery("FROM Person").list(); // 使用HQL查询

tx.commit();
return list;
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/**
* 分页的查询数据列表
* @param firstResult 从结果列表中的哪个索引开始取数据
* @param maxResults 最多取多少条数据
* @return 一页的数据列表
*/
@SuppressWarnings("unchecked")
public QueryResult findAll(int firstResult, int maxResults) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
// 查询一页的数据列表
// 方式一:
// Query query = session.createQuery("FROM User");
// query.setFirstResult(firstResult);
// query.setMaxResults(maxResults);
// List<User> list = query.list(); // 使用HQL查询

// 方式二:方法链
List<Person> list = session.createQuery( //
"FROM Person") //
.setFirstResult(firstResult) //
.setMaxResults(maxResults) //
.list();

// 查询总记录数
// session.createQuery("SELECT COUNT(*) FROM User").list().get(0);
// Long count = (Long) session.createQuery("SELECT COUNT(*) FROM User").uniqueResult();
Long count = (Long) session.createQuery( //
"SELECT COUNT(*) FROM Person") //
.uniqueResult();
tx.commit();
// 返回结果
return new QueryResult(count.intValue(), list);
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}
}
  • 测试代码
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
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import org.junit.Test;
import xyz.shi.dao.PersonDao;
import xyz.shi.domain.Person;
import xyz.shi.domain.IdCard;
import xyz.shi.entity.QueryResult;

import java.util.List;

public class PersonDaoTest {
private PersonDao personDao = new PersonDao();

@Test
public void saveTest() {
// 先创建idcard
IdCard idCard = new IdCard();
idCard.setCardNo("1111111");

// 创建persion
Person person = new Person();
person.setName("王大伟");

person.setIdCard(idCard);
personDao.save(person);
}
@Test
public void findIdTest() {
personDao.getById(16);
}

// 修改操作
@Test
public void updateTest() {

Person person = new Person();
person.setId(16);
person.setName("郑1");
personDao.update(person);

}

// 删除操作---根据id进行删除
@Test
public void deleteTest() {
personDao.delete(19);
}

// 查询所有User
@Test
public void findAllTest() {
List<Person> list = personDao.findAll();
for (Person user : list) {
System.out.println(user);
}

}
@Test
public void findAll1() {
QueryResult result = personDao.findAll(0, 5);
System.out.println(result.getCount());
for (Object o : result.getList()) {
System.out.println(o);
}
}
}

一对多

  • 在三种关联关系中,一对多(或者多对一)是最常见的一种关联关系。

  • 在关系型数据库中,一对多映射关系通常是由“多”的一方指向“一”的一方。在表示“多”的一方的数据表中增加一个外键,指向“一”的一方的数据表的主键,“一”的一方称为主表,而“多”的一方称为从表

  • 准备如下两个表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
student 表为学生表,id 为学生表的主键,name 表示学生名称,gid 为学生表的外键,指向班级表的主键 id;
grade 表为班级表,id 为班级表的主键,name 表示班级名称;

create table grade(
id int primary key auto_increment comment '主键id',
name varchar(128) comment '班级'
);

create table student(
id int primary key auto_increment comment '主键id',
name varchar(128) comment '学生名字',
gid int comment '班级id',
foreign key(gid) references grade(id)
)comment 'gid是student表的外键';


  • 新增Student实体类
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
package xyz.shi.domain;


public class Student {
private Integer id;
private String name;
//持有实体类 Grade 的一个引用,维护多对一关系
private Grade grade;

public Student() {
}

public Student(Integer id, String name) {
this.id = id;
this.name = name;
}

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 Grade getGrade() {
return grade;
}

public void setGrade(Grade grade) {
this.grade = grade;
}

@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", grade=" + grade +
'}';
}
}
  • 新增Grade实体类
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
package xyz.shi.domain;


import java.util.HashSet;
import java.util.Set;

public class Grade {
private Integer Id;
private String name;
//持有 Student 引用的集合,来维护一对多关联关系
private Set<Student> students = new HashSet<>();

public Integer getId() {
return Id;
}

public void setId(Integer id) {
Id = id;
}

public String getName() {
return name;
}

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

public Set<Student> getStudents() {
return students;
}

public void setStudents(Set<Student> students) {
this.students = students;
}

@Override
public String toString() {
return "Grade{" +
"Id=" + Id +
", name='" + name +
'}';
}
}
  • 创建 Student 的映射文件 src-man-resources-Student.hbm.xml,配置如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="xyz.shi.domain.Student" table="student" schema="shi_jdbc" lazy="true">
<!--主键映射-->
<id name="id" column="id" type="java.lang.Integer">
<!--主键生成策略-->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="100" type="java.lang.String"></property>

<!--维护关联关系-->
<many-to-one name="grade" class="xyz.shi.domain.Grade" column="gid" cascade="all"/>
</class>
</hibernate-mapping>

注意many-to-one用法,cascade=”all”为级联关系

  • 创建 Grade 的映射文件 src-man-resources-Grade.hbm.xml,配置如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="xyz.shi.domain.Grade" table="grade" schema="shi_jdbc">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"></generator>
</id>
<property name="name" column="name" length="100" type="java.lang.String"/>
<!--使用 set 元素维护一对多关联关系-->
<set name="students">
<key column="gid"></key>
<one-to-many class="xyz.shi.domain.Student"></one-to-many>
</set>
</class>
</hibernate-mapping>

从 Grade(班级)的角度看,Student 和 Grade 是一对多的关系,因此 Grade 的映射文件中,需要通过 标签来维护 Grade 与 Student 的一对多关联关系。

  • 在 Hibernate 核心配置文件 src-man-resources-hibernate.cfg.xml 中,使用 元素来指定映射文件 Student.hbm.xml 和 Grade.hbm.xml 的位置信息,配置代码如下。

    1
    2
    3
    4
    ...
    <!-- 配置hibernate的映射文件所在的位置 -->
    <mapping resource="Student.hbm.xml"/>
    <mapping resource="Grade.hbm.xml"/>
  • Dao代码

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
package xyz.shi.dao;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Person;
import xyz.shi.domain.Student;
import xyz.shi.entity.QueryResult;
import xyz.shi.utils.HibernateUtils;

import java.util.List;

public class StudentDao {
public void save(Student student) {
Session session = HibernateUtils.openSession();
try {
Transaction tx = session.beginTransaction(); // 开启事务
session.save(student);
tx.commit(); // 提交事务
} catch (RuntimeException e) {
session.getTransaction().rollback(); // 回滚事务
throw e;
} finally {
session.close(); // 关闭session
}
}

/*
* 更新
*/
public void update(Student student) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Student student1 = session.get(Student.class, student.getId());
// 设置学生信息
student1.setName(student.getName());
// 设置班级
Grade grade = student1.getGrade();
student.setGrade(grade);
//我们修改了该对象的属性,并且不需要显式调用 session.saveOrUpdate(student),Hibernate 会自动将修改后的对象同步到数据库。
// session.update(student);// 操作
tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 删除
*/
public void delete(int id) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

Object user = session.get(Student.class, id); // 要先获取到这个对象
session.delete(user); // 删除的是实体对象

tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 根据id查询,返回对象为List object,然后直接转换到各自的实体类
*/
public List<Object[]> getById(int id) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
List<Object[]> list = session.createQuery(
"select s, g from Student s join s.grade g where s.id="+id).list();
tx.commit();
return list;
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 查询所有
*/
public List<Object[]> findAll() {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
// 注意这里的用法用的原生语句,要给别名不然报错
List<Object[]> list = session.createSQLQuery("select s.id as student_id, s.name as student_name, g.id as grade_id, g.name as grade_name from student s join grade g on s.gid=g.id").list();
tx.commit();
return list;
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/**
* 分页的查询数据列表
* @param firstResult 从结果列表中的哪个索引开始取数据
* @param maxResults 最多取多少条数据
* @return 一页的数据列表
*/
@SuppressWarnings("unchecked")
public QueryResult findAll(int firstResult, int maxResults) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

List<Object[]> list = session.createQuery( //
"select s, g from Student s join s.grade g") //
.setFirstResult(firstResult) //
.setMaxResults(maxResults) //
.list();

// 查询总记录数
Long count = (Long) session.createQuery( //
"SELECT COUNT(*) FROM Student") //
.uniqueResult();
tx.commit();


// 返回结果
return new QueryResult(count.intValue(), list);
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}
}

  • session.createQuery( “select s, g from Student s join s.grade g”) 注意这里的用法,不支持join,s.grade其实就是Student实体类类设置的
  • createSQLQuery 支持用原生语句查询
  • 一对多查询,返回List<Object[]>,然后各自的实体类去转换
  • 测试
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
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import org.junit.Test;
import xyz.shi.dao.StudentDao;
import xyz.shi.domain.Student;
import xyz.shi.domain.Grade;
import xyz.shi.entity.QueryResult;

import java.util.List;

public class StudentDaoTest {
private StudentDao studentDao = new StudentDao();

@Test
public void saveTest() {
Grade grade = new Grade();
grade.setName("三年级");

Student student = new Student();
student.setName("王大伟");
//设置学生的班级
student.setGrade(grade);

Student student2 = new Student();
student2.setGrade(grade);
student2.setName("小红");

studentDao.save(student);
studentDao.save(student2);
}
@Test
public void findIdTest() {
List<Object[]> students = studentDao.getById(12);
for (Object[] result : students) {
Student student = (Student) result[0];
Grade grade = (Grade) result[1];
System.out.println(student);
System.out.println(grade);
}
}

// 修改操作
@Test
public void updateTest() {
Student student = new Student();
student.setId(3);
student.setName("郑122");
studentDao.update(student);


}

// 删除操作---根据id进行删除
@Test
public void deleteTest() {
studentDao.delete(2);
}

// 查询所有
@Test
public void findAllTest() {

List<Object[]> list = studentDao.findAll();
for (Object[] result : list) {
int studentId = (int) result[0];
String studentName = (String) result[1];
int gradeId = (int) result[2];
String gradeName = (String) result[3];

Student student = new Student();
student.setId(studentId);
student.setName(studentName);

Grade grade = new Grade();
grade.setId(gradeId);
grade.setName(gradeName);

student.setGrade(grade);
System.out.println(student);
System.out.println(grade);

}

}
@Test
public void findAll1() {
QueryResult result = studentDao.findAll(0, 5);
System.out.println(result.getCount());
List<Object[]> list = result.getList();
for (Object[] arr : list) {
Student student = (Student) arr[0];
System.out.println("Student: " + student.getName() + ", Grade: " + student.getGrade().getName());

}

}
}


多对多

  • 在实际的应用中,“多对多”也是一种常见的关联关系,例如学生和课程的关系,一个学生可以选修多门课程,一个课程可以被多名学生选修。
  • 在关系型数据库中,是无法直接表达“多对多”关联关系的,我们一般会采用新建一张中间表,将一个“多对多”关联拆分为两个“一对多”关联解决此问题,如下图

image-20231226111134866

  • 创建如下数据表关系,图书和作者,他们是多对多的关系
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
-- 创建学生表,用上面的一对多的表即可
create table student(
id int primary key auto_increment comment '主键id',
name varchar(128) comment '学生名字',
gid int comment '班级id',
foreign key(gid) references grade(id)
)
-- 创建课程表
create table course(
id int primary key auto_increment comment '主键id',
name varchar(32) comment '课程名'
);

-- 建立第三张表来保存两张表的关系
create table student_course(
id int primary key auto_increment comment '联表的id',
student_id int comment '学生的id',
course_id int comment '课程的id',
foreign key(student_id) references student(id)
on update cascade
on delete cascade,
foreign key(course_id) references course(id)
on update cascade
on delete cascade
);

  • 创建课程实体类
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
package xyz.shi.domain;

import java.util.HashSet;
import java.util.Set;

/**
* 课程实体类
*/
public class Course {
private Integer id;
private String name;
//学生 Student 的集合作为其属性,维护多对多关联关系
private Set<Student> students = new HashSet<>();
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 Set<Student> getStudents() {
return students;
}

public void setStudents(Set<Student> students) {
this.students = students;
}

@Override
public String toString() {
return "Course{" +
"id=" + id +
", name='" + name +
'}';
}
}
  • 修改 Student 实体类的代码,在 Student 类中以 Set 的形式引入 Course 对象作为其属性,来维护 Student 与 Course 之间的多对多关联关系,代码如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
package xyz.shi.domain;
....
public class Student {
//将 Course 对象的集合作为其属性,以维护它们之间的多对多关联关系
private Set<Course> courses = new HashSet<>();
public Set<Course> getCourses() {
return courses;
}

public void setCourses(Set<Course> courses) {
this.courses = courses;
}

  • 创建 Course 的映射文件 src-man-resources-Coures.hbm.xml,具体配置如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="xyz.shi.domain">
<class name="Course" table="course" schema="jdbc">
<id name="id" column="id" >
<generator class="native"></generator>
</id>

<property name="name" column="name" length="100"/>

<set name="students" table="student_course" cascade="save-update" inverse="true">
<key column="course_id"></key>
<many-to-many class="Student" column="student_id"></many-to-many>
</set>
</class>
</hibernate-mapping>

inverse(反转)属性,它的作用是控制关联的双方由哪一方管理关联关系。

inverse=”true” 如果两个映射文件的inverse都设为false(默认),则会出现异常(主键重复)导致插入失败

两映射文件中的inverse都为true,则Student和Course都去维护关联关系,即同时向连接表中插入记录,则会导致主键重复而插入失败。

将其中一方的inverse设为true,让对方维持关联关系;

  • 修改student.hbm.xml文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="xyz.shi.domain.Student" table="student" schema="shi_jdbc" lazy="true">
<!--主键映射-->
<id name="id" column="id" type="java.lang.Integer">
<!--主键生成策略-->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="100" type="java.lang.String"></property>
<!--维护关联关系-->
<many-to-one name="grade" class="xyz.shi.domain.Grade" column="gid"/>
<set name="courses" table="student_course" lazy="false">
<key column="student_id"></key>
<many-to-many class="xyz.shi.domain.Course" column="course_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
  • 测试
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import org.junit.Test;
import xyz.shi.dao.StudentDao;
import xyz.shi.domain.Grade;
import xyz.shi.domain.Student;
import xyz.shi.domain.Course;
import xyz.shi.entity.QueryResult;
import xyz.shi.utils.HibernateUtils;

import java.util.List;
import java.util.Set;

public class StudentDao2Test {

@Test
public void saveTest() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();

//新建学生和班级信息
Grade grade = new Grade();
grade.setName("三年级");
Student student = new Student();
student.setName("选课学生1");
student.setGrade(grade);
Student student2 = new Student();
student2.setName("选课学生2");
student2.setGrade(grade);
grade.getStudents().add(student);
grade.getStudents().add(student2);

//新建三个课程
Course course = new Course();
course.setName("Java");
Course course2 = new Course();
course2.setName("PHP");
Course course3 = new Course();
course3.setName("C++");

//保存操作
session.save(student);
session.save(student2);
session.save(grade);
session.save(course);
session.save(course2);
session.save(course3);

//学生选课的关系相互关联
course.getStudents().add(student);
student.getCourses().add(course);

course2.getStudents().add(student2);
student2.getCourses().add(course2);

course3.getStudents().add(student2);
student2.getCourses().add(course3);

//提交事务
transaction.commit();
//释放资源
session.close();

}
@Test
public void findIdTest() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
String hql = "SELECT s, c, g FROM Student s " +
"JOIN s.courses c " +
"JOIN s.grade g where s.name = :name";
Query query = session.createQuery(hql);
query.setParameter("name", "选课学生2");

List<Object[]> students = query.list();
for (Object[] result : students) {
Student student = (Student) result[0];
Course course = (Course) result[1];
Grade grade = (Grade) result[2];
System.out.println(student);
System.out.println(course);
System.out.println(grade);
}

//提交事务
transaction.commit();
//释放资源
session.close();

}

// 修改操作
@Test
public void updateTest() {
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
Transaction tx = session.getTransaction();
if (!tx.isActive()) {
tx.begin();
}

Student student = session.get(Student.class, 42);
student.setName("我是谁1");
// 设置学生的班级
Grade grade = session.get(Grade.class, 2);
student.setGrade(grade);

Course course = session.get(Course.class, 33);

// 先移除旧关联关系
for (Course existingCourse : student.getCourses()) {
existingCourse.getStudents().remove(student);
}

// 添加新关联关系
student.getCourses().clear();
student.getCourses().add(course);
course.getStudents().add(student);


// 保存实体对象到数据库中
session.saveOrUpdate(student);
session.saveOrUpdate(course);

if (tx.isActive()) {
tx.commit();
session.close();
sessionFactory.close();
}
}

// 删除操作
@Test
public void deleteTest() {
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
Transaction tx = session.getTransaction();
if (!tx.isActive()) {
tx.begin();
}

Student student = session.get(Student.class, 40);
Course course = session.get(Course.class, 31);

// 移除关联关系
student.getCourses().remove(course);
course.getStudents().remove(student);
// 删除学生信息
session.delete(student);

if (tx.isActive()) {
tx.commit();
session.close();
sessionFactory.close();
}
}

// 查询所有
@Test
public void findAllTest() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
// 根据业务来编写代码
String hql = "SELECT s, c, g FROM Student s " +
"JOIN s.courses c " +
"JOIN s.grade g";
Query query = session.createQuery(hql);
List<Object[]> students = query.list();
for (Object[] result : students) {
Student student = (Student) result[0];
Course course = (Course) result[1];
Grade grade = (Grade) result[2];
System.out.println(student);
System.out.println(course);
System.out.println(grade);
}
//提交事务
transaction.commit();
//释放资源
session.close();

}
@Test
public void findAll1() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();

String hql = "SELECT s, c, g FROM Student s " +
"JOIN s.courses c " +
"JOIN s.grade g";
Query query = session.createQuery(hql);
query.setFirstResult(0);
query.setMaxResults(3);
List<Object[]> students = query.list();
for (Object[] result : students) {
Student student = (Student) result[0];
Course course = (Course) result[1];
Grade grade = (Grade) result[2];
System.out.println(student);
System.out.println(course);
System.out.println(grade);
}
//提交事务
transaction.commit();
//释放资源
session.close();

}
}

总结

简单搭建

  • 环境:java 1.8 ,idea 社区版本,mysql 8.0
  • pom.xml依赖文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.2.Final</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
  • 核心配置文件src-main-java-resources-hibernate.cfg.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"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 配置关于数据库连接的四个项:driverClass url username password -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>

<!-- 可以将向数据库发送的SQL语句显示出来 -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化SQL语句 -->
<property name="hibernate.format_sql">true</property>
<!-- hibernate的方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 配置hibernate的映射文件所在的位置 -->
<mapping resource="User.hbm.xml"/>
</session-factory>
</hibernate-configuration>

  • 实体类
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
package xyz.shi.domain;

public class User {
private int id;
private String name;
private String password;
public int getId() {
return id;
}
public User () {

}
public User(int id,String name, String password) {
this.name = name;
this.id = id;
this.password = password;
}

public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", password=" + password +
'}';
}
}
  • src-main-java-resources-User.hbm.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"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="xyz.shi.domain">
<!--
name:即实体类的全名
table:映射到数据库里面的那个表的名称
catalog:数据库的名称
-->
<class name="User" table="users" catalog="test">
<!-- class下必须要有一个id的子元素 -->
<!-- id是用于描述主键的 -->
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"/>
</id>
<!--
使用property来描述属性与字段的对应关系
如果length忽略不写,且你的表是自动创建这种方案,那么length的默认长度是255
-->
<property name="name" column="name" length="20"/>
<property name="password" column="password" length="20"/>
</class>
</hibernate-mapping>

  • 测试:src-test-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
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
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import org.junit.Test;
import xyz.shi.domain.User;
import java.util.List;

public class HibernateTest {

// 保存一个User
@Test
public void saveUserTest() {
// 创建一个User
User c = new User();
c.setName("叶子");
c.setPassword("1111");
// 使用Hibernate的API来完成将User信息保存到mysql数据库中的操作
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
// 开启事务
session.beginTransaction();
// 操作
session.save(c);
// 事务提交
session.getTransaction().commit();
session.close();
sessionFactory.close();
}

// 根据id查询一个User对象
@Test
public void findUserByIdTest() {
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
// 开启事务
session.beginTransaction();
// 根据业务来编写代码
User c = session.get(User.class, 10);
System.out.println(c.getName());
// 事务提交
session.getTransaction().commit();
session.close();
sessionFactory.close();
}

// 修改操作
@Test
public void updateUserTest() {
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
// 开启事务
session.beginTransaction();
// 根据业务来编写代码
User c = session.get(User.class, "62");
c.setName("郑敏");
session.update(c); // 修改操作
// 事务提交
session.getTransaction().commit();
session.close();
sessionFactory.close();
}

// 删除操作---根据id进行删除
@Test
public void deleteUserTest() {
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
// 开启事务
session.beginTransaction();
// 根据业务来编写代码
User c = session.get(User.class, "62");
session.delete(c); // 删除操作
// 事务提交
session.getTransaction().commit();
session.close();
sessionFactory.close();
}

// 查询所有User
@Test
public void findAllUserTest() {
Configuration config = new Configuration().configure(); // Hibernate框架加载hibernate.cfg.xml文件
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相当于得到一个Connection
// 开启事务
session.beginTransaction();
// 根据业务来编写代码
Query query = session.createQuery("from User"); // HQL语句,它类似于SQL语句
List<User> list = query.list();
for (User user : list) {
System.out.println(user);
}
// 事务提交
session.getTransaction().commit();
session.close();
sessionFactory.close();
}

}

优化代码

  • 提取公共类HibernateUtils
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
package xyz.shi.domain.utils;


import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtils {

// SessionFactory全局只需要有一个就可以了,因为它的创建和销毁需要消耗大量的资源,初始化信息会比较多,并且它是线程安全的,可以在多线程的环境下使用它
private static SessionFactory sessionFactory;

static {
// 初始化SessionFactory方式一:
/*
Configuration cfg = new Configuration(); // 代表配置文件的一个对象
cfg.configure(); // 读取默认的配置文件(hibernate.cfg.xml)
// cfg.configure("hibernate.cfg.xml"); // 读取指定位置的配置文件
sessionFactory = cfg.buildSessionFactory();
*/

// 初始化SessionFactory方式二:
sessionFactory = new Configuration() //
.configure() //
.buildSessionFactory(); // 方法链
}

/**
* 获取全局唯一的SessionFactory
*
* @return
*/
public static SessionFactory getSessionFactory() {
return sessionFactory;
}

/**
* 从全局唯一的SessionFactory中打开一个Session
*
* @return
*/
public static Session openSession() {
return sessionFactory.openSession();
}

}

-

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
package xyz.shi.entity;
import java.util.List;

public class QueryResult {

private int count; // 总记录数
private List list; // 一页的数据

public void setCount(int count) {
this.count = count;
}

public int getCount() {
return count;
}
public void setList(List list) {
this.list = list;
}

public List getList() {
return list;
}

public QueryResult(int count, List list) {
this.count = count;
this.list = list;
}

@Override
public String toString() {
return "QueryResult{" +
"count=" + count +
", list=" + list +
'}';
}
}
  • dao层UserDao
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
package xyz.shi.dao;


import org.hibernate.Session;
import org.hibernate.Transaction;
import xyz.shi.domain.User;
import xyz.shi.entity.QueryResult;
import xyz.shi.utils.HibernateUtils;

import java.util.List;

public class UserDao {

/*
* 保存
*/
public void save(User user) {
Session session = HibernateUtils.openSession();
try {
Transaction tx = session.beginTransaction(); // 开启事务
session.save(user);
tx.commit(); // 提交事务
} catch (RuntimeException e) {
session.getTransaction().rollback(); // 回滚事务
throw e;
} finally {
session.close(); // 关闭session
}
}

/*
* 更新
*/
public void update(User user) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

session.update(user);// 操作

tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 删除
*/
public void delete(int id) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

Object user = session.get(User.class, id); // 要先获取到这个对象
session.delete(user); // 删除的是实体对象

tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 根据id查询一个User数据
*/
public User getById(int id) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
User user = (User) session.get(User.class, id);// 操作
tx.commit();
return user;
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/*
* 查询所有
*/
public List<User> findAll() {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();

// 方式一:使用HQL语句
// 这里的FROM User 并不是表的名字,而是User.hbm.xml中的<class name="User" 这个name
List<User> list = session.createQuery("FROM User").list(); // 使用HQL查询

tx.commit();
return list;
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}

/**
* 分页的查询数据列表
* @param firstResult 从结果列表中的哪个索引开始取数据
* @param maxResults 最多取多少条数据
* @return 一页的数据列表
*/
@SuppressWarnings("unchecked")
public QueryResult findAll(int firstResult, int maxResults) {
Session session = HibernateUtils.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
// 查询一页的数据列表
// 方式一:
// Query query = session.createQuery("FROM User");
// query.setFirstResult(firstResult);
// query.setMaxResults(maxResults);
// List<User> list = query.list(); // 使用HQL查询

// 方式二:方法链
List<User> list = session.createQuery( //
"FROM User") //
.setFirstResult(firstResult) //
.setMaxResults(maxResults) //
.list();

// 查询总记录数
// session.createQuery("SELECT COUNT(*) FROM User").list().get(0);
// Long count = (Long) session.createQuery("SELECT COUNT(*) FROM User").uniqueResult();
Long count = (Long) session.createQuery( //
"SELECT COUNT(*) FROM User") //
.uniqueResult();
tx.commit();

// 返回结果
return new QueryResult(count.intValue(), list);
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
session.close();
}
}
}

session.createQuery(“FROM User”).list(),这里的FROM User 并不是表的名字,而是User.hbm.xml中的<class name=”User” 这个name

  • 测试
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

import org.junit.Test;
import xyz.shi.dao.UserDao;
import xyz.shi.domain.User;
import xyz.shi.entity.QueryResult;

import java.util.List;

public class UserDaoTest {

private UserDao userDao = new UserDao();

@Test
public void save() {
User user = new User();
user.setName("2222");
user.setPassword("11");
userDao.save(user);
}

@Test
public void update() {
User byId = userDao.getById(10);
byId.setName("456");
userDao.update(byId);
}

@Test
public void delete() {
userDao.delete(3);
}

@Test
public void getById() {
System.out.println(userDao.getById(10));
}

@Test
public void findAll() {
List<User> list = userDao.findAll();
for (User user : list) {
System.out.println(user);
}
}

@Test
public void findAll1() {
QueryResult result = userDao.findAll(0, 5);
System.out.println(result.getCount());
for (Object o : result.getList()) {
System.out.println(o);
}
}
}

  • 以上代码主要来自这里

说明

  • 本次教程主要来自这里
  • 环境信息:win10 、java1.8、IDEA 社区版本

整合JDBC

  • 用IDEA 社区版本新建个maven项目
  • 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
<?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>org.example</groupId>
<artifactId>spring-quickstart1</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<!--数据库连接池 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
  • 实体类
1
2
3
4
5
6
7
8
9
package xyz.shi.entity;

import lombok.Data;
@Data
public class User {
private int id;
private String name;
private String password;
}
  • springconfig配置类,主要用于扫描路径
1
2
3
4
5
6
7
8
9
10
package xyz.shi.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("xyz.shi")
public class SpringConfig {
}

  • jdbc的配置文件
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
package xyz.shi.config;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

//表明当前类是spring的一个配置类,作用是替代spring的applicationContext.xml。
// 但其本质就是@Component注解,被此注解修饰的类,也会被存入spring的ioc容器
@Configuration
public class JDBCConfig {
//@Bean通常出现在Spring的配置类当中,注解在方法上,表示把当前方法的返回值存入spring的ioc容器
@Bean
public DataSource dataSource() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setPassword("123456");
hikariConfig.setUsername("root");
hikariConfig.setJdbcUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC");
hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver");
hikariConfig.addDataSourceProperty("autoCommit", "true");
hikariConfig.addDataSourceProperty("connectionTimeout", "5");
hikariConfig.addDataSourceProperty("idleTimeout", "60");
return new HikariDataSource(hikariConfig);
}
@Bean
public JdbcTemplate jdbcTemplate(@Autowired DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
  • service代码
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
package xyz.shi.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Service;
import xyz.shi.entity.User;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

@Service
public class UserService1 {
@Autowired
private JdbcTemplate jdbcTemplate;

public int save(String name, String pwd) {
//该类返回新增记录时的自增长主键值。
KeyHolder keyHolder = new GeneratedKeyHolder();
int update = jdbcTemplate.update((connection) -> {
PreparedStatement ps = connection.prepareStatement("insert into users(name,password) values(?,?)", Statement.RETURN_GENERATED_KEYS);

ps.setObject(1, name);
ps.setObject(2, pwd);
return ps;
}, keyHolder);
if (update > 0){
System.out.println("保存成功...user id:"+keyHolder.getKey());
}
return keyHolder.getKey().intValue();
}

/**
* 根据用户id 查询
* @param id
* @return
*/
public User getUser(int id){
User user = jdbcTemplate.queryForObject("select * from users where id = ?", new Object[]{id},new BeanPropertyRowMapper<>(User.class));
return user;
}

/**
* 分页查询
* @param page
* @param pageSize
* @return
*/
public List<User> queryUserList(int page, int pageSize){
int index = (page-1)*pageSize;
int size = pageSize;
List<User> list = jdbcTemplate.query("select * from users limit ?,?", new Object[]{index, size}, new BeanPropertyRowMapper<>(User.class));
return list;
}

/**
* 更新
* @param id
* @param name
* @return
*/
public boolean update(int id,String name){
int update = jdbcTemplate.update("update users set name=? where id=?", name, id);
if (update > 0){
return true;
}
throw new RuntimeException("update error");
}

public boolean delete(int id){
int deleteCount = jdbcTemplate.update("delete from users where id=?", id);
return deleteCount > 0;
}
// execute的用法
public List<User> execute1(int page,int pageSize) {
return jdbcTemplate.execute((Connection conn) -> {
PreparedStatement ps = conn.prepareStatement("select * from users limit ?,?");
ps.setObject(1, (page - 1) * pageSize);
ps.setObject(2, pageSize);
ResultSet resultSet = ps.executeQuery();
List<User> userList = new ArrayList<>();
while (resultSet.next()) {
User user = new User();
user.setId(resultSet.getInt("id"));
user.setName(resultSet.getString("name"));
user.setPassword(resultSet.getString("password"));
userList.add(user);
}
return userList;
});
}
}

注意execute的用法

  • 测试
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
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import xyz.shi.config.SpringConfig;
import xyz.shi.entity.User;
import xyz.shi.service.UserService1;

import java.util.List;

public class jdbcTest {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

UserService1 userService = context.getBean(UserService1.class);
@Test
public void save() {
int id = userService.save("test1234","22222");
System.out.println(id);
}
@Test
public void find() {
User user = userService.getUser(46);
System.out.println(user);
}
@Test
public void queryUserList() {
List<User> users = userService.queryUserList(1, 2);
System.out.println(users);
}
@Test
public void update() {
userService.update(46, "哈哈");
}
@Test
public void delete() {
boolean flag = userService.delete(46);
System.out.println(flag);

}
@Test
public void execute1() {
List<User> users1 = userService.execute1(1,3);
System.out.println(users1);
}
}

整合Mybatis

SqlSessionFactory是mybatis的核心,当与spring进行整合时,我们使用mybatis-spring提供的SqlSessionFactoryBean 来创建其实例,SqlSessionFactoryBean实现了FactoryBean 接口

  • 依赖包
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
	<?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>org.example</groupId>
<artifactId>spring-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<!--spring整合mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>

<!--mybatis环境-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!--分页插件坐标-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.1</version>
</dependency>
</dependencies>
<build>

<!--目的是把src/main/java目录中的xml文件包含到输出结果中,也就是输出到classes目录中-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>

</build>
</project>
  • 下面是xml的配置方式,resources/application.xml,主要配置了连接数据库、SqlSessionFactory等信息
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
<?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="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&amp;serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="autoCommit" value="true"/>
<property name="connectionTimeout" value="5000" />
<property name="idleTimeout" value="60" />
</bean>
<!--jdbc的xml配置-->
<bean id="myDataSource" class="com.zaxxer.hikari.HikariDataSource">
<constructor-arg name="configuration" ref="hikariConfig"/>
</bean>
<!--mybatis-->
<!--SqlSessionFactory-->
<!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory-->
<bean id="SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--set注入,把数据库连接池付给dataSource属性-->
<property name="dataSource" ref="myDataSource"/>
<!--mybatis主配置文件的位置
configLocation属性是Resource类型,读取配置文件
它的赋值使用的是value , 指定文件的路径,使用的是classpath:表示文件的位置
-->
<property name="configLocation" value="classpath:mybatis.xml"/>
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<props>
<prop key="helperDialect">mysql</prop>
</props>
</property>
</bean>
</array>
</property>
</bean>
<!--创建 dao对象
使用SqlSession的getMapper(userDao.class)
MapperScannerConfigurer在内部调用getMapper()生成每个dao接口的代理对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定的是SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="SqlSessionFactory"/>
<!--指定包名,包名是dao接口所在的包名
MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
一次getMapper()方法,得到每个接口的dao对象
创建好的dao对象放入到spring的容器中

dao默认对象的名称:是接口名字的首字母小写
-->

<property name="basePackage" value="xyz.shi.dao"/>
<!--多个包-->
<!--<property name="basePackage" value="com.md.dao,com.md.dao2"/>-->

</bean>
<!--下面的就是自己定义的service-->
<!--声明service-->
<bean id="userService" class="xyz.shi.service.Impl.UserServiceImpl">
<!--就是上面通过创建的dao对象-->
<property name="userDao" ref="userDao"/>
</bean>
</beans>
  • resources/mybatis.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" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>


<!--settings:控制mybatis全局行为-->
<settings>

<!--设置mybatis输出日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>


<!--设置别名-->
<typeAliases>
<!--
package:把包下面的所有类名作为别名
name:实体类所在的包名-->
<package name="xyz.shi.entity"/>

</typeAliases>



<!-- sql映射文件的位置 -->
<mappers>
<!-- 注册userMapper.xml文件 -->
<mapper resource="mapper/UserMapper.xml"/>
</mappers>

</configuration>

  • resources/mapper/userMapper.xml,写入sql信息
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"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="xyz.shi.dao.UserDao">

<insert id="save" parameterType="xyz.shi.entity.User" keyProperty="id" useGeneratedKeys="true">
insert into users(name,password)
value(#{name},#{password})
</insert>
<update id="update" parameterType="xyz.shi.entity.User">
update users set name=#{name},password=#{password} where id=#{id}
</update>
<delete id="delete" parameterType="int">
delete from users where id=#{id}
</delete>
<select id="findById" parameterType="int" resultType="xyz.shi.entity.User">
select * from users where id = #{id};
</select>
<select id="findAll" resultType="xyz.shi.entity.User">
select * from users
</select>
</mapper>

记得一定要把resources目录设置为Resources Root,不然读取application.xml报错不存在此文件

  • 写mapper的接口,其实就是dao层,我用的dao
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package xyz.shi.dao;

import com.github.pagehelper.Page;
import xyz.shi.entity.User;

// 注意这里的方法名称,一定要和mapper.xml中的id名称一致,不然无法映射到就会报错
public interface UserDao {
int save(User user);
int update(User user);
User findById(int id);
Page<User> findAll();
int delete(int id);
}

  • Service的Mybatis实现,增删改查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package xyz.shi.service;

import com.github.pagehelper.Page;
import org.springframework.stereotype.Service;
import xyz.shi.entity.User;

@Service
public interface UserService {
int save(User user);
Page<User> findAll();
int update1(User user);
int delete(int id);
}


  • userdao的实现接口
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
package xyz.shi.service.Impl;

import com.github.pagehelper.Page;
import xyz.shi.dao.UserDao;
import xyz.shi.entity.User;
import xyz.shi.service.UserService;

public class UserServiceImpl implements UserService {
// 引用类型
private UserDao userDao;
// 为了使用set注入来赋值,一定要加这个
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public int save(User user) {
return this.userDao.save(user);
}

@Override
public Page<User> findAll() {
return userDao.findAll();
}

@Override
public int update1(User user) {
return userDao.update(user);
}

@Override
public int delete(int id) {
return userDao.delete(id);
}
}

  • 测试
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
iimport com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xyz.shi.entity.User;
import xyz.shi.service.UserService;

import java.util.Arrays;
import java.util.List;

public class MybatisTest {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
// 其实就是调用的app..xml中的<bean id="userService" class="xyz.shi.service.Impl.UserServiceImpl">
UserService userMybatisService = (UserService) context.getBean("userService");

@Test
public void save() {
User user = new User();
user.setName("test009");
user.setPassword("1234566");
int num = userMybatisService.save(user);
System.out.println(num);
}
@Test
public void findAll() {
//在查询之前,设置分页条件 显示第一页,展示3条数据
Page<Object> page = PageHelper.startPage(1, 3);
List<User> userList = userMybatisService.findAll();
// 设置分页导航数量5
PageInfo<User> pageInfo = new PageInfo<>(userList, 5);
for (User user:userList) {
System.out.println("------每个用户的信息-------");
System.out.println(user.getName());
System.out.println(user.getPassword());
}
System.out.println();
///xxx
System.out.println("---------全面配置信息:--------");
System.out.println("总数据量 :"+pageInfo.getTotal());
System.out.println("每页的数量:"+pageInfo.getPageSize());
System.out.println("当前页码:"+pageInfo.getPageNum());
System.out.println("是否有上一页:"+pageInfo.isHasPreviousPage());
System.out.println("上一页是:"+pageInfo.getPrePage());
System.out.println("是否有下一页:"+pageInfo.isHasNextPage());
System.out.println("下一页是:"+pageInfo.getNextPage());
System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
System.out.println("导航页的第一个页码是:"+pageInfo.getNavigateFirstPage());
System.out.println("导航页的最后一个页码是:"+pageInfo.getNavigateLastPage());
System.out.println("每页显示的页码个数:"+pageInfo.getNavigatePages());
System.out.println("页码数:"+ Arrays.toString(pageInfo.getNavigatepageNums()));
}

@Test
public void update() {
User user = new User();
user.setId(41);
user.setName("test41");
user.setPassword("111111");
userMybatisService.update1(user);
}
@Test
public void delete() {
userMybatisService.delete(48);
}
}


总结

  • 注意resources目录的设置要为Resources Root
  • UserDao中的方法一定要和mapper.xml中的id一致
  • 源代码

AOP说明

  • 面向切面编程:基于OOP基础之上新的编程思想,OOP面向的主要对象是类,而AOP面向的主要对象是切面,在处理日志、安全管理、事务管理等方面有非常重要的作用。

  • AOP是Spring中重要的核心点,虽然IOC容器没有依赖AOP,但是AOP提供了非常强大的功能,用来对IOC做补充。

  • 通过Proxy的方式,将重复的公共的代码抽离出去,动态的织入XXService(业务逻辑)中,而不改变原有的Service中的代码结构

AOP的通知类型

  • 前置通知(Before advice): 在连接点之前运行但无法阻止执行流程进入连接点的通知(除非它引发异常)。
  • 后置返回通知(After returning advice):在连接点正常完成后执行的通知(例如,当方法没有抛出任何异常并正常返回时)。
  • 后置异常通知(After throwing advice): 在方法抛出异常退出时执行的通知。
  • 后置通知(总会执行)(After (finally) advice): 当连接点退出的时候执行的通知(无论是正常返回还是异常退出)。
  • 环绕通知(Around Advice):环绕连接点的通知,例如方法调用。这是最强大的一种通知类型,。环绕通知可以在方法调用前后完
  • 自定义的行为。它可以选择是否继续执行连接点或直接返回自定义的返回值又或抛出异常将执行结束。

依赖包

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
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- spring架包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>

实例

  • 本次实例为日志的使用方式

  • 编写dao Calculator接口

1
2
3
4
5
6
7
8
9
package xyz.shi.dao;

public interface Calculator {
public int add(int i,int j);
public int sub(int i,int j);
public int mul(int i,int j);
public int div(int i,int j);
}

  • LogUtil代码,加上Component注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package xyz.shi.util;

import java.lang.reflect.Method;
import java.util.Arrays;
import org.springframework.stereotype.Component;
@Component
public class LogUtil {
public static void start(Method method, Object ... objects){
System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(objects));
}
public static void stop(Method method,Object ... objects){
System.out.println(method.getName()+"方法执行完成,参数是:"+ Arrays.asList(objects));
}

public static void logException(Method method,Exception e){
System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
}
public static void end(Method method){
System.out.println(method.getName()+"方法执行结束了......");
}
}
  • MyCalculatorService代码添加@Service注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package xyz.shi.service;

import org.springframework.stereotype.Service;

@Service(value = "myCalculatorService")
public class MyCalculatorService {
public int add(int i, int j) {
return i + j;
}
public int sub(int i, int j) {
return i - j;
}
public int mul(int i, int j) {
return i * j;
}
public int div(int i, int j) {
return i / j;
}
}
  • 添加自动扫描的配置,resources-application.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--扫描整个目标文件夹 -->
    <context:component-scan base-package="xyz.shi" />
    </beans>
  • 在LogUtil.java中添加@Aspect注解

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
package xyz.shi.util;


import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
@Component
@Aspect
public class LogUtil {
@Before("execution(public int xyz.shi.service.MyCalculatorService.*(int,int))")
public static void start(){
System.out.println("方法开始执行,参数是:");
}
@AfterReturning("execution(public int xyz.shi.service.MyCalculatorService.*(int,int))")
public static void stop(){
System.out.println("方法执行完成,结果是:");

}
@AfterThrowing("execution(public int xyz.shi.service.MyCalculatorService.*(int,int))")
public static void logException(){
System.out.println("方法出现异常:");
}
@After("execution(public int xyz.shi.service.MyCalculatorService.*(int,int))")
public static void end(){
System.out.println("方法执行结束了......");
}
}




  • 开启基于注解的aop的功能,resources-application.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
<?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"
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">
<!--扫描整个目标文件夹 -->
<context:component-scan base-package="xyz.shi" />
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

  • 执行测试
1
2
3
4
5
6
7
8
9
10
11
12
13
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xyz.shi.dao.Calculator;

public class MyTest {
@Test
public void test01() {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Calculator calculator=context.getBean( "myCalculatorService",Calculator.class);
calculator.add(1, 2);
}
}
  • 结果
1
2
3
4
方法开始执行,参数是:
方法执行完成,结果是:
方法执行结束了......

cglib来创建代理对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package xyz.shi.service;

import org.springframework.stereotype.Service;

@Service
public class MyCalculatorService {
public int add(int i, int j) {
return i + j;
}

public int sub(int i, int j) {
return i - j;
}

public int mul(int i, int j) {
return i * j;
}

public int div(int i, int j) {
return i / j;
}
}

只需要把我们的MyCalculatorService.java中实现的Calculator.java的接口去掉就行了

  • 测试代码,直接调用service层代码
1
2
3
4
5
6
7
8
9
10
11
12
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xyz.shi.service.MyCalculatorService;

public class MyTest {
@Test
public void test01() {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
MyCalculatorService myCalculator = context.getBean("myCalculatorService", MyCalculatorService.class);
myCalculator.add(1,2);
}
  • 当然也可以不写xml配置文件,直接写在class中,比如
1
2
3
4
5
6
7
8
9
10
11
12
package xyz.shi.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("xyz.shi")
//注意开启AOP的支持,并且代理设置为cglib动态代理,因为UserService没有接口
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {
}
  • 测试代码需要改变下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import xyz.shi.config.SpringConfig;
import xyz.shi.service.MyCalculatorService;

public class MyTest {
@Test
public void test01() {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
MyCalculatorService myCalculator = context.getBean("myCalculatorService", MyCalculatorService.class);
myCalculator.add(1,2);
}
}

xml的AOP配置

  • 这里没有实践
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"
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">

<context:component-scan base-package="xyz.shi"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

<bean id="myCalculatorService" class="xyz.shi.service.MyCalculatorService"></bean>
<bean id="logUtil" class="xyz.shi.util.LogUtil"></bean>

<aop:config>

<aop:aspect ref="logUtil">
<aop:pointcut id="logPoint" expression="execution(public int xyz.shi.service.MyCalculatorService.*(int,int))"/>
<aop:before method="start" pointcut-ref="logPoint"></aop:before>
<aop:after method="end" pointcut-ref="logPoint"></aop:after>
<aop:after-returning method="stop" pointcut-ref="logPoint" returning="result"></aop:after-returning>
<aop:after-throwing method="logException" pointcut-ref="logPoint" throwing="e"></aop:after-throwing>
<aop:around method="myAround" pointcut-ref="logPoint"></aop:around>
</aop:aspect>
</aop:config>
</beans>

切点表达式

  • 切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)

execution

  • execution(public User com.mszlu.service.UserService.findById(int))

  • execution在实际工作中,很少被使用,因为匹配的打击面非常大或者非常小,不能灵活应用

  • execution(public * com.mszlu.service.*.*(..)) 基本能实现无差别全覆盖,即某个包下面的所有Bean的所有方法都会被拦截。

    execution(public * update*(..))从方法的前缀来区分,这种误伤的概率非常大,你不可能要求所有的程序员都按照这种书写习惯来。

annotation

  • 我们使用AOP的时候,常常使用annotation的形式

  • 场景:比如我们实现一个性能监控的需求,使用AOP实现

  1. 定义注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package com.mszlu.aop;

    import java.lang.annotation.*;

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface MsMetric {
    String value() default "";
    }
  2. 在需要监控的方法上,加上注解

    1
    2
    3
    4
    @MsMetric
    public void registerUser(String mail,String password,String nickname){
    //...
    }
  3. 实现性能监控AOP,切点使用annotation

    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
    package com.mszlu.aop;

    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;

    @Aspect
    @Component
    @Slf4j
    public class MetricAspect {

    // 定义切点 注解所在的方法 即是切点
    @Pointcut("@annotation(MsMetric)")
    public void pt() {}

    //定义通知
    @Around("pt()")
    public Object doLogging(ProceedingJoinPoint pjp) throws Throwable{
    try {
    log.info("----------------metric start--------------------");
    //计算方法执行的时间
    long startTime = System.currentTimeMillis();
    //方法调用
    Object ret = pjp.proceed();
    long endTime = System.currentTimeMillis();
    log.info("方法执行时间:{}ms", endTime-startTime);
    log.info("----------------metric end--------------------");
    return ret;
    }catch (Exception e){
    log.error("异常信息",e);
    throw e;
    }
    }
    }