一、整合持久层框架MyBatis
1.准备数据库表及数据
创建数据库:springboot
使用IDEA工具自带的mysql插件来完成表的创建和数据的准备:
创建表
表创建成功后,为表准备数据,如下:
2.创建SpringBoot项目
使用脚手架创建Spring Boot项目
引入mysql驱动以及mybatis的启动器
依赖如下:
<!--mybatis的启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!--mysql的驱动依赖-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
注意,之前也提到过:
Spring Boot官方提供的启动器的名字规则:spring-boot-starter-xxx
第三方(非Spring Boot官方)提供的启动器的名字规则:xxx-spring-boot-starter
3.编写数据源配置
前面提到过,Spring Boot配置统一可以编写到application.properties中,配置如下:
以上的配置属于连接池的配置,连接池使用的是Spring Boot默认的连接池:HikariCP
4.编写实体类Vip
package org.example1.bean;
public class Vip {
private Long id;
private String name;
private String cardNumber; // 数据库字段 card_number
private String birth;
public Vip(Long id, String name, String cardNumber, String birth) {
this.id = id;
this.name = name;
this.cardNumber = cardNumber;
this.birth = birth;
}
public Vip() {
}
public Vip(String name, String cardNumber, String birth) {
this.name = name;
this.cardNumber = cardNumber;
this.birth = birth;
}
@Override
public String toString() {
return "Vip{" +
"id=" + id +
", name='" + name + '\'' +
", cardNumber='" + cardNumber + '\'' +
", birth='" + birth + '\'' +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCardNumber() {
return cardNumber;
}
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
public String getBirth() {
return birth;
}
public void setBirth(String birth) {
this.birth = birth;
}
}
以上代码可以使用第三方库Lombok进行改造,后面再说。
5.编写Mapper接口
创建repository
包,在该包下新建VipMapper
接口,代码如下:
package org.example1.repository;
import org.example1.bean.Vip;
import java.util.List;
public interface VipMapper {
/**
* 保存会员信息
* @param vip 会员信息
* @return 1表示保存成功。
*/
int insert(Vip vip);
/**
* 获取所有会员信息
* @return 会员列表
*/
List<Vip> selectAll();
}
6.编写Mapper接口的XML配置文件
在`resources`目录下新建`mapper`目录,将来的`mapper.xml`配置文件放在这个目录下。
安装`MyBatisX`插件,该插件可以根据我们编写的`VipMapper`接口自动生成mapper的XML配置文件。
然后在`VipMapper`接口上:alt+enter
接下来,你会看到Mapper接口中方法报错了,可以在错误的位置上使用`alt+enter`,选择`Generate statement`:
接下来就是编写SQL语句了,最终VipMapper.xml
文件的配置如下:
7.添加Mapper的扫描
在Spring Boot的入口程序上添加如下的注解,来完成`VipMapper`接口的扫描:
8.编写service
接口
package org.example1.service;
import org.example1.bean.Vip;
import java.util.List;
public interface VipService {
/**
* 保存会员信息
* @param vip 会员信息
* @return true表示成功,false表示失败
*/
boolean save(Vip vip);
/**
* 查看会员列表
* @return 会员列表
*/
List<Vip> findAll();
}
package org.example1.service.impl;
import org.example1.bean.Vip;
import org.example1.repository.VipMapper;
import org.example1.service.VipService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class VipServiceImpl implements VipService {
@Autowired
private VipMapper vipMapper;
@Override
public boolean save(Vip vip) {
return vipMapper.insert(vip) == 1;
}
@Override
public List<Vip> findAll() {
return vipMapper.selectAll();
}
}
9.告诉MyBatis框架MapperXML文件的位置
在`application.properties`配置文件中进行如下配置:
10.测试整合MyBatis是否成功
在Spring Boot主入口程序中获取Spring上下文对象ApplicationContext
,从Spring容器中获取VipService
对象,然后调用相关方法进行测试:
测试结果中可以看到cardNumber
属性没有赋值成功,原因是:表中的字段名叫做card_number
,和实体类Vip
的属性名cardNumber
对应不上。解决办法两个:
-
第一种方式:查询语句使用as关键字起别名,让查询结果列名和实体类的属性名对应上。
再次测试:
-
第二种方式:通过配置自动映射
在application.properties
配置文件中进行如下配置:
mybatis.configuration.map-underscore-to-camel-case=true
map-underscore-to-camel-case 是一个配置项,主要用于处理数据库字段名与Java对象属性名之间的命名差异。在许多数据库中,字段名通常使用下划线(_)分隔单词,例如 first_name 或 last_name。而在Java代码中,变量名通常使用驼峰式命名法(camel case),如 firstName 和 lastName。
当使用MyBatis作为ORM框架时,默认情况下它会将SQL查询结果映射到Java对象的属性上。如果数据库中的字段名与Java对象的属性名不一致,那么就需要手动为每个字段指定相应的属性名,或者使用某种方式来自动转换这些名称。
map-underscore-to-camel-case 这个配置项的作用就是在查询结果映射到Java对象时,自动将下划线分隔的字段名转换成驼峰式命名法。这样可以减少手动映射的工作量,并提高代码的可读性和可维护性。
mapper的xml文件中的sql语句仍然使用*
的方式:
测试结果如下:
二、 Lombok库
Lombok 是一个 Java 库,它可以通过注解的方式减少 Java 代码中的样板代码。Lombok 自动为你生成构造函数、getter、setter、equals、hashCode、toString 方法等,从而避免了手动编写这些重复性的代码。这不仅减少了出错的机会,还让代码看起来更加简洁。
Lombok只是一个编译阶段的库,能够帮我们自动补充代码,在Java程序运行阶段并不起作用。(因此Lombok库并不会影响Java程序的执行效率)
例如我们有这样一个java源文件`User.java`,代码如下:
@Data
public class User{
private String name;
}
以上代码在程序的编译阶段,Lombok库会将User.java
文件编译生成这样的User.class
字节码文件:
public class com.powernode.lomboktest.model.User {
public com.powernode.lomboktest.model.User();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public java.lang.String getName();
Code:
0: aload_0
1: getfield #7 // Field name:Ljava/lang/String;
4: areturn
public void setName(java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield #7 // Field name:Ljava/lang/String;
5: return
public boolean equals(java.lang.Object);
Code:
0: aload_1
1: aload_0
2: if_acmpne 7
5: iconst_1
6: ireturn
7: aload_1
8: instanceof #8 // class com/powernode/lomboktest/model/User
11: ifne 16
14: iconst_0
15: ireturn
16: aload_1
17: checkcast #8 // class com/powernode/lomboktest/model/User
20: astore_2
21: aload_2
22: aload_0
23: invokevirtual #13 // Method canEqual:(Ljava/lang/Object;)Z
26: ifne 31
29: iconst_0
30: ireturn
31: aload_0
32: invokevirtual #17 // Method getName:()Ljava/lang/String;
35: astore_3
36: aload_2
37: invokevirtual #17 // Method getName:()Ljava/lang/String;
40: astore 4
42: aload_3
43: ifnonnull 54
46: aload 4
48: ifnull 65
51: goto 63
54: aload_3
55: aload 4
57: invokevirtual #21 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
60: ifne 65
63: iconst_0
64: ireturn
65: iconst_1
66: ireturn
protected boolean canEqual(java.lang.Object);
Code:
0: aload_1
1: instanceof #8 // class com/powernode/lomboktest/model/User
4: ireturn
public int hashCode();
Code:
0: bipush 59
2: istore_1
3: iconst_1
4: istore_2
5: aload_0
6: invokevirtual #17 // Method getName:()Ljava/lang/String;
9: astore_3
10: iload_2
11: bipush 59
13: imul
14: aload_3
15: ifnonnull 23
18: bipush 43
20: goto 27
23: aload_3
24: invokevirtual #24 // Method java/lang/Object.hashCode:()I
27: iadd
28: istore_2
29: iload_2
30: ireturn
public java.lang.String toString();
Code:
0: aload_0
1: invokevirtual #17 // Method getName:()Ljava/lang/String;
4: invokedynamic #28, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
9: areturn
}
通过字节码可以看到Lombok库的@Data
注解可以帮助我们生成无参构造器
、setter
、getter
、toString
、hashCode
、equals
。
1.Lombok 的主要注解
@Data:
-
等价于
@ToString
,@EqualsAndHashCode
,@Getter
,@Setter
,@RequiredArgsConstructor
. -
用于生成:必要参数的构造方法、getter、setter、toString、equals 和 hashcode 方法。
@Getter / @Setter:
-
分别用于生成所有的 getter 和 setter 方法。
-
可以作用于整个类,也可以作用于特定的字段。
@NoArgsConstructor:
-
生成一个无参构造方法。
@AllArgsConstructor:
-
生成一个包含所有实例变量的构造器。
@RequiredArgsConstructor:
-
生成包含所有被
final
修饰符修饰的实例变量的构造方法。 - 如果没有的实例变量,则自动生成无参数构造方法。
@ToString / @EqualsAndHashCode:
- 用于生成 toString 和 equals/hashCode 方法。
- 这两个注解都有exclude属性,通过这个属性可以定制toString、hashCode、equals方法。
Lombok 主要注解详解及代码示例
添加依赖
在 Maven 的 pom.xml
文件中添加 Lombok 依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
IDEA中安装Lombok插件
高版本的IntelliJ IDEA工具默认都是绑定Lombok插件的,不需要再额外安装:
Lombok插件不是必须要安装的,为了提高开发效率以及开发者的体验,安装Lombok插件是有必要的。
也就是说安装了Lombok插件之后,编写代码的时候,才会有方法的提示功能。
2.Lombok的其他常用注解
@Value
@Builder
@Singular
@Slf4j
......
@Value
该注解会给所有属性添加final
,给所有属性提供getter
方法,自动生成toString
、hashCode
、equals
通过这个注解可以创建不可变对象。
测试程序:
可以查看一下字节码,你会发现,@Value注解的作用只会生成:全参数构造方法、getter方法、hashCode、equals、toString方法。(没有setter方法。)
@Builder
GoF23种设计模式之一:建造模式
建造模式(Builder Pattern)属于创建型设计模式。GoF23种设计模式之一。
用于解决对象创建时参数过多的问题。它通过将对象的构造过程与其表示分离,使得构造过程可以逐步完成,而不是一次性提供所有参数。建造模式的主要目的是让对象的创建过程更加清晰、灵活和可控。
简而言之,建造模式用于:
-
简化构造过程:通过逐步构造对象,避免构造函数参数过多。
-
提高可读性和可维护性:让构造过程更加清晰和有序。
-
增强灵活性:允许按需配置对象的不同部分。
这样可以更方便地创建复杂对象,并且使得代码更加易于理解和维护。
建造模式的代码
建造模式代码如下:
package org.example1;
// 定义一个 Person 类,用于表示一个人的信息
public class Person {
// 一般建造模式的 bean 属性使用 final 进行修饰,确保对象的不可变性
private final String name;
private final int age;
// 私有构造函数,只能通过建造器来创建 Person 对象
private Person(String name, int age) {
// 将传入的 name 参数赋值给当前对象的 name 属性
this.name = name;
// 将传入的 age 参数赋值给当前对象的 age 属性
this.age = age;
}
// 获取姓名的方法
public String getName() {
return name;
}
// 获取年龄的方法
public int getAge() {
return age;
}
// 重写 toString 方法,方便打印 Person 对象的信息
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// 通过这个公开的静态方法获取建造器对象
public static PersonBuilder builder() {
return new PersonBuilder();
}
// 静态内部类,用于构建 Person 对象
public static class PersonBuilder {
// 用于存储要构建的 Person 对象的姓名
private String name;
// 用于存储要构建的 Person 对象的年龄
private int age;
// 设置姓名的方法,返回当前建造器对象,以便进行链式调用
public PersonBuilder name(String name) {
this.name = name;
return this;
}
// 设置年龄的方法,返回当前建造器对象,以便进行链式调用
public PersonBuilder age(int age) {
this.age = age;
return this;
}
// 构建 Person 对象的方法
public Person build() {
return new Person(name, age);
}
}
}
建造模式就是可以把参数拆出来,一个一个建造?
没错,建造模式的确能够把参数拆分,逐个进行构建。下面为你详细介绍建造模式及其参数拆分构建的原理:
定义
建造模式(Builder Pattern)属于创建型设计模式,其作用是将一个复杂对象的构建和表示分离,如此一来,相同的构建过程就能创建出不同的表示。
把参数拆出来逐个构建的原理
- 链式调用:建造模式一般会在建造器类里为每个参数提供对应的设置方法,这些方法会返回建造器自身,这样就能实现链式调用。你可以逐个调用这些方法来设置参数。
- 封装构建逻辑:构建复杂对象时,不同参数的设置可能涉及复杂的逻辑,建造模式把这些逻辑封装在建造器类的各个方法中,用户仅需调用这些方法就能完成参数设置,而不用关心内部的具体实现。
- 最终构建:当所有参数都设置好之后,调用建造器的
build()
方法,该方法会依据设置好的参数创建出最终的对象。
使用@Builder注解自动生成建造模式的代码
该注解可以直接帮助我们生成以上的代码。使用@Builder
注解改造以上代码。
@Singular
@Singular注解是辅助@Builder注解的。
当被建造的对象的属性是一个集合,这个集合属性使用@Singular注解进行标注的话,可以连续调用集合属性对应的方法完成多个元素的添加。如果没有这个注解,则无法连续调用方法完成多个元素的添加。代码如下:
先说不使用Singular的方法
运行结果:
使用@Singular注解
运行结果:
@Slf4j
Lombok 支持多种日志框架的注解,可以根据你使用的日志框架选择合适的注解。以下是 Lombok 提供的部分日志注解及其对应的日志框架:
@Log4j:
自动生成一个 org.apache.log4j.Logger 对象。
适用于 Apache Log4j 1.x 版本。
@Slf4j:
自动生成一个 org.slf4j.Logger 对象。
适用于 SLF4J(Simple Logging Facade for Java),这是一种日志门面,可以与多种实际的日志框架(如 Logback、Log4j 等)集成。
@Log4j2:
自动生成一个 org.apache.logging.log4j.Logger 对象。
适用于 Apache Log4j 2.x 版本。
使用示例
假设我们有一个类 `ExampleClass`,并且我们想要使用 SLF4J 作为日志框架,我们可以这样使用 `@Slf4j` 注解:
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class UserService {
public void login(){
log.info("登录验证...");
}
// 测试
public static void main(String[] args) {
UserService userService = new UserService();
userService.login();
}
}
在这个例子中,`log` 是一个静态成员变量,表示一个 `org.slf4j.Logger` 对象。Lombok 自动生成了这个日志对象,并且你可以直接使用它来进行日志记录。
选择合适的注解
选择哪个注解取决于你使用的日志框架。例如:
- 如果你使用的是 SLF4J,可以选择 `@Slf4j`。
- 如果你使用的是 Log4j 1.x,可以选择 `@Log4j`。
- 如果你使用的是 Log4j 2.x,可以选择 `@Log4j2`。
注意事项
确保在使用这些注解之前,已经在项目中引入了相应的日志框架依赖。例如,如果你使用 SLF4J,你需要在项目中添加 SLF4J 的依赖,以及一个具体的日志实现(如 Logback)。对于其他日志框架,也需要相应地添加依赖。
示例依赖
如果你使用 Maven 项目,并且选择了 SLF4J + Logback 的组合,可以添加以下依赖:
<!--Slf4j日志规范-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.16</version>
</dependency>
<!--Slf4j日志实现:logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.11</version>
</dependency>
通过这些日志注解,你可以方便地在类中使用日志记录功能,而无需手动创建日志对象。
执行结果:
三、MyBatis逆向生成
MyBatis逆向工程:使用IDEA插件可以根据数据库表的设计逆向生成MyBatis的Mapper接口 与 MapperXML文件。
1.安装插件free mybatis tools
2.在IDEA中配置数据源
3.生成MyBatis代码放到SpringBoot项目中
在表上右键:Mybatis-Generator
代码生成后,如果在IDEA中看不到,这样做(重新从硬盘加载):
注意:生成的VipMapper
接口上自动添加了@Repository
注解,这个注解没用,删除即可。
4.编写mybatis相关配置
application.properties属性文件的配置:
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot
spring.datasource.username=root
spring.datasource.password=123456
mybatis.mapper-locations=classpath:com/powernode/springboot/repository/*.xml
mybatis.configuration.map-underscore-to-camel-case=true
5.编写测试程序
对初始的Vip进行修改
测试
到此,Spring Boot整合MyBatis结束!
四、 整合SpringMVC(SSM整合)
SSM整合:Spring + SpringMVC + MyBatis
Spring Boot项目本身就是基于Spring框架实现的。因此SSM整合时,只需要在整合MyBatis框架之后,引入`web启动器`即可完成SSM整合。
1.使用脚手架创建SpringBoot项目
添加依赖:web启动器、mybatis启动器、mysql驱动依赖、lombok依赖
项目结构:
2.使用free mybatis tool
插件逆向生成MyBatis代码
将springboot
数据库中的t_vip
表逆向生成mybatis代码。这里不再赘述。
3.整合MyBatis
编写数据源的配置
在主入口类上添加@MapperScan
注解
4.编写service
编写`VipService`接口:
public interface VipService {
/**
* 根据id获取会员信息
* @param id 会员标识
* @return 会员信息
*/
Vip getById(Long id);
}
编写`VipServiceImpl`实现类:
5.编写controller
编写`VipController`,代码如下:
@RestController
public class VipController {
@Autowired
private VipService vipService;
@GetMapping("/vip/{id}")
public Vip detailById(@PathVariable("id") Long id){
Vip vip = vipService.getById(id);
return vip;
}
}
6.启动服务器测试
执行SpringBoot项目主入口的main方法,启动Tomcat服务器:
打开浏览器访问:
到此为止,SSM框架就集成完毕了,通过这个集成也可以感觉到SpringBoot简化了SSM三大框架的集成。
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误-CSDN博客