前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PHP转JAVA学习遇到的一系列问题记录

PHP转JAVA学习遇到的一系列问题记录

作者头像
躺平程序员老修
发布2023-10-18 16:46:25
2840
发布2023-10-18 16:46:25
举报

前言

之前做项目都是照葫芦画瓢,从来没系统性的学习过java、spring、springboot,现在下定决心从0开始学习,本文章只为记录个人遇到的一系列问题,并直接写出来以加深印象。

java部分和spring部分没记录,直接从spring boot开始到spring cloud结束。

知识点

  • 用idea初始化spring boot项目后,maven装的特别慢:需要配置maven镜像。参考具体配置
    • 创建了一个yml格式的配置文件application.yml,发现中文注释报错,需要设置项目的文件编码都为utf-8。参考具体配置
    • 使用@RequestMapping发现所有接口自动跳转login页面,注释pom文件中的spring-boot-starter-security,因为它默认所有接口收到保护
    • 写了一个接口发现报错:请检查控制器是否添加了@RestController注解
    • 写了一个bean,发现idea提示未配置 Spring Boot 配置注解处理器, 需要pom添加配置spring-boot-configuration-processor即可解决。参考具体配置

自定义获取默认配置项的bean

  • 写了一个获取默认配置项的bean,必须保证Java Bean的属性名称与配置一致,然后,添加两个注解@Configuration@ConfigurationProperties,就能自动对应配置文件的数据。 使用的时候,必须使用@Autowired自动注入,不能自己new出来,因为要通过springboot来处理注解等框架的东西。

如果标注@Configuration,则可通过Spring Boot的自动扫描机制自动加载,否则,使用@Import在启动入口引入手动加载

代码语言:javascript
复制
// application.yml配置文件
spring:
  data:
    redis:
      host: ${REDIS_HOST:localhost}
      port: ${REDIS_PORT:6379}
      password: ${REDIS_PASSWORD:}
      database: ${REDIS_DATABASE:0}
代码语言:javascript
复制
// bean文件夹
@Data
@Configuration
@ConfigurationProperties("spring.data.redis")
public class RedisConfigBean {
    private String host;
    private int port;
    private String password;
    private int database;
}
代码语言:javascript
复制
// 控制器使用 使用Bean来获取配置项,必须使用@Autowired自动注入
    @Autowired
    RedisConfigBean redisConfigBean;

    @Operation(summary = "Redis测试")
    @PostMapping(value = "/redis")
    public void list2() {
        System.out.println(redisConfigBean.getPort());
    }

使用@Bean注解来注册bean

  • 或者使用@Bean注解来注册一个bean:将第三方类,按照使用Bean的方式注册到容器中,只会调用一次,在其他地方就可以跟正常使用bean一样使用,先自动注入,然后就是使用。

跟上文中主要区别就是需要用@Bean进行声明,并且没有@Configuration注解

代码语言:javascript
复制
// 比如有个class,不能自己添加比如@Configuration等其他注解

public class SomeClass {
    private int a = 1;
    private int b = 2;

    public int getA() {
        return a*100;
    }

    public int getB() {
        return b*100;
    }
}
代码语言:javascript
复制
// 想让他变成一个bean注册到容器中,在config或者其他文件夹加一个挂载类
@Component
public class ThirdPartBean {
    @Bean
    public SomeClass someClass() {
        return new SomeClass();
    }
}

// 然后就可以在任何地方使用了,跟使用bean一样
@RestController
@RequestMapping("/third")
public class ThirdController {

    @Autowired
    SomeClass someClass;

    @GetMapping(value = "/getbean", produces = "application/json")
    public void getBean() {
        System.out.println(someClass.getA());
    }
}

常见注解的理解

  • @Repository、@Component、@Service、@Controller之间的区别与联系具体参考文档
代码语言:javascript
复制
在Spring2.5版本中,引入了更多的Spring类注解:@Component,@Service,@Controller。
@Component是一个通用的Spring容器管理的单例bean组件。而@Repository, @Service, @Controller就是针对不同的使用场景所采取的特定功能化的注解组件。

当你的一个类被@Component所注解,那么就意味着同样可以用@Repository, @Service, @Controller来替代它:

@Component最普通的组件,可以被注入到spring容器进行管理
@Repository作用于持久层,作为DAO对象(数据访问对象,Data Access Objects),可以直接对数据库进行操作
@Service作用于业务逻辑层,处理业务逻辑
@Controlle作用于表现层(spring-mvc的注解),前端请求的处理,转发,重定向

总结:@Service, @Controller, @Repository = {@Component + 一些特定的功能}。这个就意味着这些注解在部分功能上是一样的。都属于spring注解,注解后可以被spring框架所扫描并注入到spring容器来进行管理。

使用swagger生成接口文档

swagger3基于openApi3,大部分注解都跟老的不一样了,参考 https://blog.csdn.net/qq_35425070/article/details/105347336

QQ截图20231013103431.png
QQ截图20231013103431.png
  • 添加接口文档包,swagger
代码语言:javascript
复制
<!-- 接口文档 swagger UI 本地访问 http://localhost:8080/swagger-ui/index.html-->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.1.0</version>
</dependency>
代码语言:javascript
复制
// 代码示例
@Operation(summary = "测试自动加载通过@Bean注入的bean")
@GetMapping(value = "/getbean", produces = "application/json")
public void getBean() {
    System.out.println(someClass.getA());
}

@Operation(summary = "swagger标题摘要:path参数以及url参数", description = "下方描述:访问示例http://localhost:8080/third/list1/12?sort=11212")
@GetMapping(value = "/list1/{id}", produces = "application/json")
public List list1(
        @PathVariable(value = "id", required = true) @Parameter(description = "path参数") int id,
        @RequestParam(value = "sort", required = false, defaultValue = "0") @Parameter(description = "url参数") int sort
) {
    List list = new ArrayList();
    list.add("A");
    list.add("B");
    list.add(32);
    list.add("32");
    list.add(id);
    list.add(sort);
    System.out.println(list);
    return list;
}

Maven 相关知识

代码语言:javascript
复制
    // # 模块管理
    // 在软件开发中,把一个大项目拆分成多个模块是降低软件复杂度的有效方法
    // 对于Maven来说,原来是一个大项目
    //
    //single-project
    //├── pom.xml
    //└── src
    //
    // 现在可以分拆成三个模块
    //
    //mutiple-project
    //├── module-a
    //│   ├── pom.xml
    //│   └── src
    //├── module-b
    //│   ├── pom.xml
    //│   └── src
    //└── module-c
    //    ├── pom.xml
    //    └── src

    // Maven可以有效的管理多个模块,我们只需要把每个模块当作一个独立的Maven项目,他们有各自独立的pom.xml
    // - 首先创建一个parent项目,把abc项目中重复的提取出来,他继承springframework,并定好各个包的版本号以及引入共同依赖包(<packaging>pom</packaging>)
    // - 之后,其他项目比如a,b,c就可以继承parent项目(<packaging>jar</packaging>)
    // - 最后,在编译的时候,还要在根目录创建一个pom.xml(也就是build pom)统一编译,其中规定了 <modules>,比如
    //    <modules>
    //        <module>parent</module>
    //        <module>module-a</module>
    //        <module>module-b</module>
    //        <module>module-c</module>
    //    </modules>
    //
    // 这样,在根目录执行 mvn clean package时,maven根据根目录的pom.xml找到包括parent在内的4个<module>,一次性编译

JDBC和连接池

  • JDBC链接: JDBC是一套接口规范,类似PHP的PDO,是协议,我们把数据库实现了JDBC接口的jar包称为JDBC驱动,比如mysql的包mysql-connector-java
代码语言:javascript
复制
jdbc:mysql://<hostname>:<port>/<db>?key1=value1&key2=value2
jdbc:mysql://localhost:3306/fmock?useSSL=false&characterEncoding=utf8
  • 连接池:目前使用最广泛的就是hikariCP
代码语言:javascript
复制
    // 1. 首先
    // 添加他的依赖
    // <dependency>
    //    <groupId>com.zaxxer</groupId>
    //    <artifactId>HikariCP</artifactId>
    //    <version>4.0.3</version>
    //    <type>bundle</type>
    // </dependency>

    // 2.接着
    // 获取数据库连接
    @SneakyThrows
    public static Connection getConnection() {
        String JDBC_URL = "jdbc:mysql://localhost:3306/fmock";
        String JDBC_USER = "root";
        String JDBC_PWD = "root";

        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(JDBC_URL);
        config.setUsername(JDBC_USER);
        config.setPassword(JDBC_PWD);

        config.addDataSourceProperty("connectionTimeout", "1000"); // 连接超时:1秒
        config.addDataSourceProperty("idleTimeout", "60000");      // 空闲超时:60秒
        config.addDataSourceProperty("maximumPoolSize", "10");     // 最大连接数:10

        // 注意创建DataSource也是一个非常昂贵的操作,所以通常DataSource实例总是作为一个全局变量存储,并贯穿整个应用程序的生命周期
        DataSource ds = new HikariDataSource(config);

        // 有了连接池以后,我们如何使用它呢?
        // 和前面的代码类似,只是获取Connection时,把DriverManage.getConnection()改为ds.getConnection()
        // 第一次调用ds.getConnection(),会迫使连接池内部先创建一个Connection,再返回给客户端使用。
        // 当我们调用conn.close()方法时(在try(resource){...}结束处),不是真正“关闭”连接,而是释放到连接池中,以便下次获取连接时能直接返回
        Connection conn = ds.getConnection();
        // Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PWD);

        return conn;
    }

    // 关闭数据库连接(释放到连接池)
    public static void closeConn(Connection conn) {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

自动生成getter和setter方法的包:lombok

  • 自动在类上生成getting()和setting()方法:lombok包
代码语言:javascript
复制
// pom中添加如下依赖即可,版本号统一配置就行,在parent的dependencyManagement里有
// 注意:dependencyManagement中定义的只是依赖的声明,并不实现引入,因此子项目需要显式的声明需要用的依赖
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

// 添加完后,在类上添加 `@Data` 注解即可

关于pom配置文件抽离parent,官方spring-boot就是这么做的

  • pom使用parent统一管理的好处:properties和dependencyManagement中统一定义了依赖包的版本号

参考链接

代码语言:javascript
复制
如果有多个子项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号。
当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要逐个修改子项目;另外如果某个子项目需要另外的一个版本,只需要声明version即可。

想使用redis:

首先,redis/jedis是redis官方推出的面向JAVA的客户端; 其次,Spring-data-redis是spring大家族的一部分,包含了jedis依赖,同时还有很多其他包,有连接池自动管理功能,提供高度封装的RedisTemplate类,对jedis大量api进行归类封装,并提供很多便捷化方法; 然后,spring-boot-starter-data-redis是spring-boot的包,其中就包含了spring-data-redis依赖和lettuce等; 最后Redisson,Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。 (在spring-boot项目中,可以直接用专门的包redisson-spring-boot-starter,直接帮你了很多autoconfig的事,它里面就有spring-boot-starter-data-redis,所以可以用RedisTemplate类,清晰了吧。 当然,你也可以直接用Redisson,然后自己进行配置,new对象使用。就好比laravel的一些包,可以直接用"ORM"包,也可以用"Larave-ORM"的专属包)

代码语言:javascript
复制
// 引入依赖
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.4</version>
</dependency>
代码语言:javascript
复制
// 默认只有`RedisTemplate<Object, Object>`  和  `RedisTemplate<String, String>`, 所以需要
// 配置一个`RedisTemplate<String, Object>`类型的Bean,并初始序列化方式,否则存入的redis会有乱码
@Configuration
public class RedisTemplateConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();

        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(redisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(redisSerializer);
        //key haspmap序列化
        template.setHashKeySerializer(redisSerializer);

        return template;
    }
}
代码语言:javascript
复制
// 使用
@RestController()
@RequestMapping(value = "four")
public class FourController {

    // Java变量的初始化顺序为:静态变量或静态语句块 –> 实例变量或初始化语句块 –> 构造方法 –> @Autowired

    private RedisService redisService;

    /* 构造方法注入
    public FourController(RedisService redisService) {
        this.redisService = redisService;
    }
    */

    // 推荐使用基于set的自动注入,可以在构造函数执行之后才自动注入
    @Autowired
    private void init(RedisService redisService) {
        this.redisService = redisService;
    }

    @GetMapping(value = "redis1")
    public void redis1() {
        System.out.println(redisService.get("key111"));
        System.out.println(1221);
    }
}

使用数据库mysql spring-boot-starter-jdbc

1.首先mysql-connector-j的前身是mysql-connector-java,是MySQL提供的JDBC驱动包drive,实现了java.sql.Driver的规范,换数据库直接换drive即可。 2.spring-boot-starter-jdbcspring-boot-starter-data-jdbc都是springboot提供的,前者是基础包,后者是升级版(是data系列的包,同样的还有data-redis,data-elasticsearch等)。 3.mybatis-spring-boot-starter包含spring-boot-starter-jdbc,不再需要单独引入spring-boot-starter-jdbc了。 4.mybatis是个第三方包,任何框架都能使用,mybatis-spring-boot-starter是针对spring-boot专门配置的一个包,包含了starter自动装载等功能。 5.Spring Boot作为Spring的集大成者,spring-jdbcspring-data-jdbc 就是spring-boot-starter-jdbcspring-boot-starter-data-jdbc在spring里的东西。

代码语言:javascript
复制
// 先引入依赖包
<!--  数据库相关 mysql-connector-j是驱动包,使用mysql必须装。driver-class-name中可以指定  -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

<!--  这个包里就引入了spring-jdbc, 使用JdbcTemplate  -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
代码语言:javascript
复制
// application.yml添加数据库配置
spring:
  application:
    name: ${APP_NAME:我的测试JAVA项目}
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/fmock
    username: root
    password: root
代码语言:javascript
复制
// 已经能使用了,随便写个组件,
// 增删改都是jdbcTemplate.update方法,只有查询方法不一样,有query,queryForObject,queryForList等
@Component
public class DBService {
    @Autowired
    JdbcTemplate jdbcTemplate;

    // 在JdbcTemplate中,除了查询有几个API之外,新增、删除与修改统一都使用update来操作,传入SQL即可.
    // update方法的返回值就是SQL执行受影响的行数.

    // 插入单条
    public int addUser(User user) {
        String sql = "insert into users(uuid, email, mobile, name, password) value (?, ?, ?, ?, ?)";

        // return jdbcTemplate.update(sql);
        return jdbcTemplate.update(sql, user.getUuid(), user.getEmail(), user.getMobile(), user.getName(), user.getPassword());
    }

    // 批量插入
    public int[] batchAddUser(User user) {
        String sql = "insert into users(uuid, email, mobile, name, password) value (?, ?, ?, ?, ?)";

        List<Object[]> batchValue = new ArrayList<Object[]>();
        batchValue.add(new Object[]{"uuid-tets", "8888@qq.com", "12111111111", "测试jdbctemplate", "xxxxxxxx"});
        batchValue.add(new Object[]{user.getUuid(), user.getEmail(), user.getMobile(), user.getName(), user.getPassword()});

        return jdbcTemplate.batchUpdate(sql,batchValue);
    }

    // 更新数据
    public int updateUser(User user) {
        String sql = "update users set name = ?, email = ? where id = ?";
        return jdbcTemplate.update(sql, "修改后的名字", user.getEmail(), user.getId());
    }

    // 删除
    public int delUser(long id){
        String sql = "delete from users where id = ?";
        return jdbcTemplate.update(sql, id);
    }

    // 查询,查询就不使用update方法了
    // queryForObject查询的结果不能空,且只能1条数据:nullableSingleResult
    // 使用new BeanPropertyRowMapper<>(User.class)对返回的数据进行封装,它通过名称匹配的方式,自动将数据列映射到指定类的实体类中(如果列名和属性名不同,就需要开发者自己实现 RowMapper 接口)
    public User find(long id) {
        String sql = "select * from users where id = ?";
        RowMapper<User> rowMapper = new BeanPropertyRowMapper<>(User.class);
        return jdbcTemplate.queryForObject(sql, rowMapper, id);
    }

    // queryForList返回多条或者0条都可以
    public List<Map<String, Object>> find2(long id) {
        String sql = "select * from users where id > ?";
        return jdbcTemplate.queryForList(sql, id);
    }
    // 直接使用query方法查询,能自定义映射类型rowMapper
    public List<User> find3(long id) {
        String sql = "select * from users where id > ?";
        RowMapper<User> rowMapper = new BeanPropertyRowMapper<>(User.class);
        return jdbcTemplate.query(sql, rowMapper, id);
    }
}
代码语言:javascript
复制
// 控制器调用测试
@RestController()
@RequestMapping(value = "four")
public class FourController {

    // Java变量的初始化顺序为:静态变量或静态语句块 –> 实例变量或初始化语句块 –> 构造方法 –> @Autowired

    private RedisService redisService;

    private DBService dbService;

    /* 构造方法注入
    public FourController(RedisService redisService) {
        this.redisService = redisService;
    }
    */

    // 推荐使用基于set的自动注入,可以在构造函数执行之后才自动注入
    @Autowired
    private void init(RedisService redisService, DBService dbService) {
        this.redisService = redisService;
        this.dbService = dbService;
    }

    @GetMapping(value = "redis1")
    public void redis1() {
        System.out.println(redisService.get("key111"));
        System.out.println(1221);
    }

    @GetMapping(value = "db2")
    public int addUser() {
        User user = new User();
        user.setMobile("13333333333");
        user.setEmail("13333333333@qq.com");
        user.setName("name-db2");
        user.setPassword("xxxx");
        user.setUuid("uuid-xxxx");
        return dbService.addUser(user);
    }

    @GetMapping(value = "db3")
    public int[] batchAddUser() {
        User user = new User();
        user.setMobile("13333333333");
        user.setEmail("13333333333@qq.com");
        user.setName("name-db2");
        user.setPassword("xxxx");
        user.setUuid("uuid-xxxx");
        return dbService.batchAddUser(user);
    }

    @GetMapping(value = "db4")
    public int updateUserEmail() {
        User user = new User();
        user.setEmail("fffffff@qq.com");
        user.setId(37);
        return dbService.updateUser(user);
    }

    @Operation(summary = "根据ID删除某个用户")
    @GetMapping(value = "db5/{id}", produces = "application/json")
    public int delUserById(
            @PathVariable(value = "id") @Parameter(description = "待删除的用户ID") long id
    ) {
        return dbService.delUser(id);
    }

    @Operation(summary = "使用queryForObject查询单个用户")
    @GetMapping(value = "db6/{id}", produces = "application/json")
    public User findUserById(
            @PathVariable(value = "id") @Parameter(description = "查询ID") long id
    ) {
        return dbService.find(id);
    }

    @Operation(summary = "queryForList返回多条或者0条都可以")
    @GetMapping(value = "db7/{id}", produces = "application/json")
    public List<Map<String, Object>> getUserList(
            @PathVariable(value = "id") @Parameter(description = "查询大于ID的列表") long id
    ) {
        List<Map<String, Object>> list = dbService.find2(id);
        return list;
    }

    @Operation(summary = "query返回多条或者0条都可以")
    @GetMapping(value = "db8/{id}", produces = "application/json")
    public List<User> getUserListQuery(
            @PathVariable(value = "id") @Parameter(description = "查询大于ID的列表") long id
    ) {
        List<User> list = dbService.find3(id);
        return list;
    }
}

JPA : Java Persistence API

JPA就是JavaEE的一个ORM标准,他只规定了接口,所以还需要选择一个实现产品,跟JDBC接口和Mysql驱动一个意思。 HibernateMyBatis都是优秀的第三方ORM框架,我们即可以直接使用JPA也就是jakarta.persistence这个“标准”包,也可以用第三方包如MyBatis等。

MyBatis & MyBatis-plus:ORM框架

对比Spring提供的JdbcTemplate和ORM框架,有如下差别:

  1. 查询后需要手动提供mapper实例,以便把ResultSet的每一行变成Java对象(自定义映射类型rowMapper)。
  2. 增删改操作,所有需要的参数要手动传入,比如传参user.id/user.name等这样。
  3. 但是JdbcTemplate的优势是确定性,即每次读取操作一定是数据库操作而不是缓存,确定是代码比较繁琐。

介于全自动ORM如Hibernate,和手写全部SQL的比如JdbcTemplate之间,还有一种半自动的ORM,它只负责把ResultSet自动映射到JavaBean,或者自动填充JavaBean参数,但仍需要自己写Sql, MyBatis就是这种半自动化ORM框架。

首先需要引入mybatis-spring-boot-starter包,这个包里有mybatis-springmybatis, 还有spring-boot-starter-jdbc,以及自动配置的一些starter、autoconfigure包。

代码语言:javascript
复制
// 首先,spring-boot项目中使用的话,需要安装 `MyBatis-Spring-Boot-Starter` 依赖,之后就可以使用`@Mapper`注解(默认搜寻带有 @Mapper 注解的 mapper 接口)
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>
代码语言:javascript
复制
// 创建一个mapper比如/mapper/UserMapper.java,注意mapper必须是接口
// `@Select`中即可以写sql,支持使用`#{}`占位符参数,多个参数时可以用`@Param`进行标记匹配,示例如下:
// `@Select("SELECT * FROM users LIMIT #{offset}, #{maxResults}")`
// `List<User> getAll(@Param("offset") int offset, @Param("maxResults") int maxResults);`
package com.example.springbootstudy.mapper;

import com.example.springbootstudy.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;

@Mapper
public interface UserMapper {
    @Select(value = "select count(id) from users")
    int getAllCount();

    @Select("select name from users where id = #{id}")
    String getUserName(long id);

    // mybatis会根据方法的返回类型自动把ResultSet的每一行转换为User实例,不需要像jdbc那样使用RowMapper手动映射
    // 如果列名和属性名不同,可以通过select语句的别名`as`满足匹配
    @Select("select * from users where id = #{user_id}")
    User getUserInfo(@Param("user_id") long id);

    @Select("select * from users where id > #{id}")
    List<User> getUserList(@Param("id") long id);

    // 使用@Options注解,可以获取插入后的id,通过user对象
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    @Insert("insert into users(uuid, email, mobile, password, name) values(#{userObj.uuid}, #{userObj.email}, #{userObj.mobile}, #{userObj.password}, #{userObj.name})")
    void addUser(@Param("userObj") User user);

    // 更新
    @Update("UPDATE users SET name = #{user.name}, createdAt = #{user.createdAt} WHERE id = #{user.id}")
    void update(@Param("user") User user);
    // 删除
    @Delete("DELETE FROM users WHERE id = #{id}")
    void deleteById(@Param("id") long id);
}
代码语言:javascript
复制
// 创建一个控制器,依赖注入mapper,即可简单调用mybatis
@RestController
@RequestMapping(value = "/five", name = "测试mybatis的控制器")
public class FiveController {

    private final UserMapper userMapper;
    
    // 将mapper依赖注入
    public FiveController(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @GetMapping(value = "mb1", name = "测试方法mb1")
    public int mb1() {
        System.out.println("mb1");
        return userMapper.getAllCount();
    }

    @Operation(summary = "获取用户名字")
    @GetMapping(value = "mb2")
    public String mb2(
            @RequestParam(value = "id") @Parameter(description = "url参数") long id
    ) {
        return userMapper.getUserName(id);
    }

    @Operation(summary = "获取用户信息")
    @GetMapping(value = "mb3/{id}")
    public User mb3(
            @PathVariable(value = "id") @Parameter(description = "path参数") long id
    ) {
        return userMapper.getUserInfo(id);
    }

    @Operation(summary = "获取用户列表")
    @GetMapping(value = "mb4/{id}")
    public List<User> mb4(
            @PathVariable(value = "id") @Parameter(description = "path参数") long id
    ) {
        return userMapper.getUserList(id);
    }

    @Operation(summary = "新增用户,并返回ID")
    @GetMapping(value = "mb5")
    public int mb5() {
        User user = new User();
        user.setEmail("litblc@xxx.com");
        user.setUuid("litblc-uuid");
        user.setMobile("13111111111");
        user.setPassword("xxx");
        user.setName("大修哥");

        userMapper.addUser(user);

        // 获取刚刚插入的user自增id
        return user.getId();
    }
}

更多资料参考 https://www.liaoxuefeng.com/wiki/1252599548343744/1331313418174498

使用xml方式使用mybatis

在application.yml中添加mapper-locations配置,指定待解析的mapper的xml文件

代码语言:javascript
复制
mybatis:
  # 待解析的mapper的xml文件
  mapper-locations: classpath:/mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true
    default-fetch-size: 100
    default-statement-timeout: 30

在同级文件夹(也就是resources)下创建mapper文件夹,可以用别的名称,与上面配置的路径名称对应上即可。 创建一个UserMapper.xml文件(注意的是<!DOCTYPE mapper标签必须有,而且指定是mapper):

代码语言:javascript
复制
<?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="com.example.springbootstudy.mapper.UserMapper">
    <select id="findByUUID" resultType="com.example.springbootstudy.dao.UserInfoDao">
        SELECT * FROM users WHERE uuid=#{uuid}
    </select>
    <select id="getAll" resultType="com.example.springbootstudy.dao.UserInfoDao">
        SELECT * FROM users order by id desc
    </select>
</mapper>

这时,你的UserMapper.java中定义这个findByUUID接口,就不需要使用注解定义sql了:

代码语言:javascript
复制
@Mapper
public interface UserMapper {
    @Select(value = "select count(id) from users")
    int getAllCount();

    @Select("select name from users where id = #{id}")
    String getUserName(long id);

    // ...

    // 测试使用xml中配置sql语句
    UserInfoDao findByUUID(@Param("uuid") String uuid);

    // 返回map的话必须指定map的key用哪个字段
    // List<UserInfoDao> getAll();
    @MapKey("id")
    Map<Integer, UserInfoDao> getAll();
}

使用上是一样的:

代码语言:javascript
复制
@Operation(summary = "测试使用mybatis的xml方式")
@GetMapping(value = "mb6/{id}")
public UserInfoDao mb6(
        @PathVariable(value = "id") @Parameter(description = "path参数") String uuid
) {
    System.out.println(uuid);
    return userMapper.findByUUID(String.valueOf(uuid));
}

@Operation(summary = "测试使用mybatis的xml方式-返回list/map")
@GetMapping(value = "mb7")
public Map<Integer, UserInfoDao> mb7() {
    return userMapper.getAll();
}

总结:MyBatis完全手写,每增加一个查询都需要先编写SQL并增加接口方法。

更多参考 https://blog.csdn.net/m0_37989980/article/details/105588234

mybatis-plus:更方便的使用mybatis

更多了解参考官网 https://baomidou.com/

mybatis-plus具有无侵入、损耗小、只做增强不做改变、强大的 CRUD 操作、丰富的查询构造器等特点,在国内十分火爆。

  • 引入依赖
代码语言:javascript
复制
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.2</version>
</dependency>
  • 创建实体模型entity
代码语言:javascript
复制
package com.example.springbootstudy.entity;

//import jakarta.persistence.Table;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

// `@TableName`是mp的注解,主要是实现实体类型和数据库中的表实现映射。
// 不要将`@TableName`和`@Table`注解认为是一个,虽然功能相同,但是,`@TableName`是`mybatis-plus`中的注解, `@Table`是`Hibernate`中的注解(JPA)

@Data
@TableName(value = "posts")
public class Poster {
    private Long id;
    private String uuid;
    private Long user_id;
    private String title;
    private String summary;
    private String poster;
    private String content;
}
  • 创建与实体entity对应的mapper接口:PostsMapper
代码语言:javascript
复制
package com.example.springbootstudy.mapper;

import com.example.springbootstudy.entity.Poster;
import org.apache.ibatis.annotations.Mapper;

// `@Mapper`注解可以在每个mapper上定义,也可以在`Spring Boot`启动类中添加 `@MapperScan`直接扫描整个文件夹
@Mapper
public interface PostsMapper extends IBaseMapper<Poster> {
    // 添加复杂的操作数据库方法,就可以在xml中写,跟mybatis用法一样
}

上面继承的IBaseMapper是自定义的,继承自mybatis-plus的BaseMapper,提供了一些封装好的通用的curd操作,通过源码可以看到。

代码语言:javascript
复制
package com.example.springbootstudy.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

// mapper父类,注意这个类不要让 mp 扫描到!!
public interface IBaseMapper<T> extends BaseMapper<T> {
    // 这里可以放一些自定义的公共的方法
}
  • 控制器使用测试(一般是由service调用,这里偷懒了直接控制器写逻辑测试用)
代码语言:javascript
复制
// 使用@RestController替代@Controller后,每个方法自动变成API接口方法。直接返回json。
// @Controller
@RestController
@RequestMapping("/six")
public class SixController {

    private final PostsMapper postsMapper;

    public SixController(PostsMapper postsMapper) {
        this.postsMapper = postsMapper;
    }

    @GetMapping(value = "f3")
    public List<Poster> func3() {
        List<Poster> list = postsMapper.selectList(null);
        return list;
    }
}
  • mybatis-plus进阶学习

插入一条数据自增ID, 并学习使用lombok的@Accessors(chain = true)注解

代码语言:javascript
复制
@Data
// 链式访问,该注解设置chain=true,生成setter方法返回this(也就是返回的是对象),代替了默认的返回void
@Accessors(chain = true)
@TableName(value = "posts")
public class Poster {
    // 指定自增策略,这样insert的时候id才是自增的
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    private String uuid;

    // 比如没开启自动转驼峰的配置项map-underscore-to-camel-case:false,或者名字不一样可以自行指定
    @TableField(value = "user_id")
    private Long userId;

    private String title;
    private String summary;
    private String poster;
    private String content;

    // 自动维护创建、更新时间
    // 如何返回创建后的实体
}

添加mybatis-plus的配置,配置带解析的mapper的xml文件地址,方便进行自定义复杂sql查询

代码语言:javascript
复制
mybatis:
  # 待解析的mapper的xml文件
  mapper-locations: classpath:/mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true
    default-fetch-size: 100
    default-statement-timeout: 30
    ...

mybatis-plus:
  # 待解析的mapper的xml文件,跟上面一样意思
  mapper-locations: classpath:/mapper/*.xml
  configuration:
    map-underscore-to-camel-case: false

在PostsMapper中添加一个自定义sql的方法,并在PostsMapper.xml中补全

代码语言:javascript
复制
@Mapper
public interface PostsMapper extends IBaseMapper<Poster> {
    // 添加复杂的操作数据库方法,就可以在xml中写,跟mybatis用法一样
    List<UserInfoDao> getAll();
}

新建一个xml,主要注意namespace别写错喽,不然找不到

代码语言:javascript
复制
<mapper namespace="com.example.springbootstudy.mapper.PostsMapper">
    <select id="getAll" resultType="com.example.springbootstudy.dao.UserInfoDao">
        SELECT * FROM users order by id desc
    </select>
</mapper>

控制器调用示例

代码语言:javascript
复制
    @Operation(summary = "新增文章")
    @GetMapping(value = "f4")
    public Poster func4() {
        // 在entity上定义了`@Accessors(chain = true)`,就可以直接链式调用所有方法,因为setter方法返回当前对象了
        Poster poster = new Poster().setPoster("https://xxx/xxx.jpg");
        poster.setUuid("xxx-uuid-11");
        poster.setTitle("测试").setSummary("摘要").setUserId(10L).setContent("{\"a\":32}");

        postsMapper.insert(poster);  // 返回插入数量
        return poster;
    }

    @Operation(summary = "测试xml方式的sql查询")
    @GetMapping(value = "f6")
    public List<UserInfoDao> func6() {
        return this.postsMapper.getAll();
    }
  • JSON格式字段咋处理

当数据库存储json时候,建议字段仍然用textvarchar等字符串格式,entity对应也用String,保存入库的时候正常按照字符串类型操作; 查询的时候想返回JSON格式的话,可以再写个dto, 把字段改成JSONObject格式,然后手动赋值进去(JSONObject是使用com.alibabafastjson依赖)。

如果数据库字段格式是json,那entity也必须是json格式的了,比如:

  1. 引入json包
代码语言:javascript
复制
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.41</version>
        </dependency>
  1. entity中的jsonInfo字段是json格式
代码语言:javascript
复制
@Data
@Accessors(chain = true)
@TableName(value = "reports", autoResultMap = true)
public class Report {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long userId;
    private Long resourceId;
    private String reason;
    private String type;

    // json字段要使用`com.alibaba.fastjson.JSONObject`的这个类型,可以不定义json具体字段格式,并且不会报错没序列化错误
    // 同时要设置`typeHandler`,这样获取的时候就是json,只不过存的时候需要同样生成该类型json入库
    @TableField(value = "json_info", typeHandler = JacksonTypeHandler.class)
    private JSONObject jsonInfo;
  1. 创建mapper接口,注入entity
代码语言:javascript
复制
@Mapper
public interface ReportMapper extends IBaseMapper<Report>{

}
  1. 控制器读写
代码语言:javascript
复制
package com.example.springbootstudy.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.springbootstudy.entity.Report;
import com.example.springbootstudy.mapper.ReportMapper;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/seven")
public class SevenController {
    private final ReportMapper reportMapper;

    public SevenController (ReportMapper reportMapper) {
        this.reportMapper = reportMapper;
    }

    @GetMapping(value = "/s1")
    public Report s1() {
        return this.reportMapper.selectById(1);
    }

    @Operation(summary = "测试json字段或者json格式的字符串读写问题")
    @GetMapping(value = "/s2")
    public Report s2() {
        
        // 使用同样的json格式化类型
        JSONObject jsonInfo = JSON.parseObject("{\"a\":3222}");

        Report report = new Report().setUserId(2L)
                .setResourceId(23L).setType("post")
                .setJsonInfo(jsonInfo)
                .setReason(String.valueOf(jsonInfo));
        this.reportMapper.insert(report);
        return report;
    }
}
  • 想自动更新某些字段,比如创建时间、更新时间、删除时间等

首先在entity字段中添加@TableField(fill = FieldFill.INSERT)

代码语言:javascript
复制
    // 创建时自动填充
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createdAt;

    // 创建与修改时自动填充
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updatedAt;

添加了fill注解并不会字段维护,而是需要写个handle处理,比如写个组件类专门设置/component/MybatisPlusAutoFill.java

代码语言:javascript
复制
package com.example.springbootstudy.component;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.example.springbootstudy.constant.MybatisPlusAutoFillProperties;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 自定义实现类
 * @Author zhenhuaixiu
 * @Date 2023/10/16 10:55
 * @Version 1.0
 */

@Component
public class MybatisPlusAutoFill implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        // String format = "yyyy-MM-dd HH:mm:ss";
        // String now = LocalDateTime.now().format(DateTimeFormatter.ofPattern(format));

        this.strictInsertFill(metaObject, MybatisPlusAutoFillProperties.CREATED_AT, LocalDateTime::now, LocalDateTime.class);
        this.strictInsertFill(metaObject, MybatisPlusAutoFillProperties.UPDATED_AT, LocalDateTime.class, LocalDateTime.now());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        System.out.println("updateFill");
        this.strictUpdateFill(metaObject, MybatisPlusAutoFillProperties.UPDATED_AT, LocalDateTime::now, LocalDateTime.class);
    }
}

上面的自动填充类调用了MybatisPlusAutoFillProperties,是我们写的所有需要自动更新的字段常量类,比如/constant/MybatisPlusAutoFillProperties.java

代码语言:javascript
复制
package com.example.springbootstudy.constant;

public class MybatisPlusAutoFillProperties {
    // 字段要与entity对应上,咱们开启了自动转驼峰,所以是驼峰形势的
    public static final String CREATED_AT = "createdAt";
    public static final String UPDATED_AT = "updatedAt";
}

这样在我们再次调用insert或者update的时候,就能看到更新了。

mybatis-plus就写到这,其他的去官网看,特别详细 https://baomidou.com/

项目代码分层解读

更多参考 https://www.jianshu.com/p/18c4418e9b99

  1. Entity: 实体层,一个表对应一个,相当于php的model
  2. Dao:持久层,用来封装操作数据的方法,相当于php的repositories层,会调用entity。(使用mybatis的mapper层其实就相当于Dao层)
  3. Service:业务层,与php一致,调用dao层接口,接收dao层返回的数据,完成项目的基本功能设计
  4. Controller:控制层,与php一致,前后端交互,接受前端请求,调用service层并接收返回的数据
  5. 总体流程:Controller-->service接口-->serviceImpl-->dao接口-->daoImpl-->mapper-->db

Dao VS Mapper Dao通常被认为是传统的Java数据访问层,它将数据访问的逻辑封装在一组DAO接口和实现类中。这些接口和实现类主要用于将Java对象映射到数据库表,并执行一些数据操作,例如插入、更新、删除和查询。DAO通常使用JDBC和SQL语句来实现数据操作。在MyBatis中,DAO可以使用MyBatis的SqlSession和SqlSessionFactory来管理数据库连接和事务,并且可以使用MyBatis的动态SQL功能执行高度灵活的查询。 Mapper是MyBatis中的另一种数据访问层实现方式,它基于XML或注解的方式来描述SQL语句和参数映射,提供了更灵活、更简洁的数据访问方式。Mapper使用XML或注解来描述SQL语句和参数映射,并将它们映射到Java方法上。在执行数据操作时,Mapper会将Java方法转换为对应的SQL语句,并使用SqlSession执行该SQL语句。相对于Dao,Mapper更加灵活,并且在编写SQL语句时提供了更多的可读性和可维护性。 在实际开发中,选择使用Dao还是Mapper取决于具体的需求和个人偏好。如果您更喜欢面向接口的编程模型,并且需要使用Java代码来定义和执行数据操作,那么Dao可能是一个更好的选择。如果您需要更灵活、更简洁的方式来描述SQL语句,并且不介意使用XML或注解来描述它们,那么Mapper可能更适合您的需求。 这里要注意区分Dto,Dto是数据传输对象,类似response返回类,用来封装返回对象格式

Bean的生命周期理解

Spring Cloud

概念理解

  1. Spring:是JavaEE的一个轻量级开发框架,主营IoC和AOP,集成JDBC、ORM、MVC等功能便于开发
  2. Spring Boot:是基于Spring,提供开箱即用的积木式组件,目的是提升开发效率
  3. Spring Cloud:分布式应用程序,为了让分布式应用程序编写更方便,更容易而提供的一组基础设施,它的核心是Spring框架,利用Spring Boot的自动配置,力图实现最简化的分布式应用程序开发

初始化创建一个spring cloud微服务项目

  1. 首先通过IDEA创建一个maven项目,比如命名为scd(spring cloud demo),然后一般会删除src目录,顶级目录不做编程。
代码语言:javascript
复制
文件-新建-项目-Maven-下一步
  1. 在刚刚创建的项目右键新建一个模块,都选择maven项目,然后选择父项scd,创建。 IDEA会自动在顶级scd的pom文件中生成模块,并标记packaging为pom类型。这里我们先创建三个模块,分别是parent、config、common。

<packaging>pom</packaging>的理解

scd1.png
scd1.png
scd2.png
scd2.png
scd3.png
scd3.png
  1. 记得设置文件编码全都改成UTF-8 在setting - File Encodings中设置,全局的、项目的、属性文件的都改成UTF-8.
代码语言:javascript
复制
  #QR{padding-top:20px;} #QR a{border:0} #QR img{width:180px;max-width:100%;display:inline-block;margin:.8em 2em 0 2em} #rewardButton {     border: 1px solid #ccc;     /*width: 20%;*/     line-height: 53px;     text-align: center;     height: 70px;     display: block;     border-radius: 10px;     -webkit-transition-duration: .4s;     transition-duration: .4s;     background-color: #f77b83;     color: #f7f7f7;     margin: 20px auto;     padding: 8px 25px; } #rewardButton:hover {     color: #eb5055;     border-color: #f77b83;     outline-style: none;     background-color: floralwhite; 	}  
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • 知识点
      • 自定义获取默认配置项的bean
      • 使用@Bean注解来注册bean
      • 常见注解的理解
      • 使用swagger生成接口文档
      • Maven 相关知识
      • JDBC和连接池
      • 自动生成getter和setter方法的包:lombok
      • 关于pom配置文件抽离parent,官方spring-boot就是这么做的
      • 想使用redis:
      • 使用数据库mysql spring-boot-starter-jdbc
      • JPA : Java Persistence API
      • MyBatis & MyBatis-plus:ORM框架
      • 使用xml方式使用mybatis
      • mybatis-plus:更方便的使用mybatis
      • 项目代码分层解读
      • Bean的生命周期理解
      • Spring Cloud
        • 概念理解
          • 初始化创建一个spring cloud微服务项目
          相关产品与服务
          云数据库 Redis
          腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档