Spring Boot 框架整合 MyBatis 连接数据库,详细说明

news2024/11/15 9:32:33

在我们日常的开发过程中,肯定不可避免的会使用到数据库以及 SQL 语句。比如,刚开始学习 Java 的时候可能会遇到 JDBC,它是连接 Java 和数据库的桥梁,我们可以使用 JDBC 来建立与数据库之间的连接并且执行相应的 SQL 语句。虽然 JDBC 的执行效率很高,但是其开发效率比较低。正是如此,市面上出现了一大批 ORM(对象关系映射)框架,例如 Hibernate,iBATIS 以及本文将要介绍的 MyBatis。

MyBatis 简介

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。它避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。因为 MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs (Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。

通俗地说,MyBatis 就是我们使用 Java 程序操作数据库时的一种工具,可以简化我们使用 JDBC 时的很多操作,而且还简化了数据库记录与 POJO 之间的映射方式。

为什么要使用 MyBatis

前文提到过,目前市面上 有很多的 ORM 框架,例如 Hibernate,iBATIS 以及 Spring 全家桶的 Spring Data JPA。那么我们为什么要使用 MyBatis 呢?因为使用 MyBatis 有以下优势:

可以简化我们操作数据库的步骤。

相对 Hibernate 来说学习成本较低,Hibernate 还需要学习其 HQL 查询规范。

使用相对广泛。

准备工作

本文将使用开源的数据库连接池 DBCP(DataBase Connection Pool)连接 MySQL 数据库,并在此基础上整合 MyBatis 以及 MyBatis Plus,进而讲解如何使用 MyBatis 和 MyBatis Plus 操作数据库。所以在开始本教程的阅读之前,需要如下准备:

一个 Spring Boot 的 Web 项目,你可以通过 Spring Initializr 页面生成一个空的 Spring Boot 项目,当然也可以下载 springboot-pom.xml 文件,然后使用 Maven 构建一个 Spring Boot 项目。

安装 MySQL 数据库或者一台 MySQL 服务器。

使用 DBCP 连接池

DBCP 数据库连接池是 Apache 上的一个 Java 连接池项目,也是 Tomcat 使用的连接池组件。由于建立数据库连接是一种非常耗时、耗资源的行为,所以通过连接池预先同数据库建立一些连接,放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,使用完毕后再归还到连接池中。

添加依赖

这一步很简单,只需要在 pom.xml 中添加清单 1 的内容即可。

清单 1. 添加相关依赖

mysql mysql-connector-java 6.0.6 runtime org.apache.commons commons-dbcp2 2.5.0 < /dependency>

配置数据源

添加好依赖后,我们需要做的就是配置我们的数据源。首先我们需要在配置文件中添加数据源相关的配置项的值,下面清单代码只给出了部分项,完整内容可以查看本文源码:

清单 2. 数据源配置文件配置项

基本属性 spring: datasource: dbcp2: url: jdbc:mysql://localhost:3306/spring_tutorial? serverTimezone=GMT%2B8amp;characterEncoding=utf-8 username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver

添加好配置项后,我们在 cn.itweknow.sb-mybatis.config 包下新建了 DataSourceConfiguration 类,它是数据源的配置类,其内容如下,可以看到在这个类里面我们配置了数据源。

清单 3. 数据源配置

@Configuration @ConditionalOnProperty(name = “spring.datasource.dbcp2.url”, matchIfMissing = false) @MapperScan(value = { “cn.itweknow.sbmybatis.mapper” }, sqlSessionFactoryRef = “sqlSessionFactory”) public class DataSourceConfiguration { @Bean(name = “dataSource”) @ConfigurationProperties(prefix = “spring.datasource.dbcp2”) public DataSource dataSource() { return new BasicDataSource(); } }

到这一步,如果我们能够正常启动项目就意味着我们的连接池配置成功了。

集成 MyBatis

下面我们来了解如何在 Spring Boot 项目中配置 MyBatis。只需要三大步骤就可以完成这个配置。第一步是添加依赖包,第二步是配置数据库事务和会话工厂,最后一步就是配置 Mapper 的路径。

添加 MyBatis 相关依赖包

我们只需要在 pom.xml 文件的 标签下添加如下内容即可。

清单 4. 添加 MyBatis 依赖包

org.mybatis.spring.boot mybatis-spring-boot-starter 1.3.2

配置数据库事务和会话工厂

清单 5. 配置数据库事务和会话工厂

@Bean(name = “transactionManager”) public DataSourceTransactionManager dbOneTransactionManager( @Qualifier(“dataSource”) DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = “sqlSessionFactory”) @ConditionalOnMissingBean(name = “sqlSessionFactory”) public SqlSessionFactory dbOneSqlSessionFactory(@Qualifier(“dataSource”) DataSource dataSource) throws Exception { final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); return sessionFactory.getObject(); }

在 DataSourceConfiguration 类中添加上面的代码块,我们就配置了一个事务管理器和一个会话工厂。

配置 Mapper 路径

首先指定 *Mapper.java 的扫描路径(即存放 *Mapper.java 的包地址)。我们可以通过在 DataSourceConfiguration 类或者 SbMybatisApplication 类上添加 @MapperScan 注解来指定扫描路径:

清单 6. 配置 *Mapper.java 扫描路径

@MapperScan(value = { “cn.itweknow.sbaop.db.mapper” }, sqlSessionFactoryRef = “sqlSessionFactory”) public class DataSourceConfiguration { … }

通过会话工厂指定指定 *Mapper.xml 的路径。修改 SqlSessionFactory 的 Bean 创建方法如下所示:

清单 7. 配置 *Mapper.xml 扫描路径

@Bean(name = “sqlSessionFactory”) @ConditionalOnMissingBean(name = “sqlSessionFactory”) public SqlSessionFactory dbOneSqlSessionFactory(@Qualifier(“dataSource”) DataSource dataSource)throws Exception { final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources(“classpath:sqlmap/*Mapper.xml”)); return sessionFactory.getObject(); }

至此,我们的 MyBatis 和 Spring Boot 就整合完成了。下面的章节将介绍具体如何使用 MyBatis 了。MyBatis 一共提供了两种使用方式,一种是基于 XML 配置 的方式,一种是基于注解的方式。我们将分别从这两个方面来了解如何使用 MyBatis。

基于 XML 方式使用 MyBatis

准备工作

我们将通过一个具体的场景来讲解如何使用 MyBatis 进行数据库操作:定义一个学生实体和班级实体,并创建对应的数据库表,然后使用 MyBatis 对其进行增、删、改、查操作。学生类的全路径为 cn.itweknow.sbmybatis.model.dao.Student,班级类的全路径为 cn.itweknow.sbmybatis.model.dao.Clazz,相对应的数据库表结构点击这里获取。

创建 Mapper 类及 XML 文件

创建好对应的实体类后,我们还需要为其创建对应的 Mapper.java 和 Mapper.xml 文件,需要注意的是这两类文件需要放到前面配置数据源时指定的路径下,在本例中 Mapper.java 文件是放在 cn.itweknow.sbmybatis.mapper 包下,而 Mapper.xml 则是放在 resources/sqlmap 目录下。

创建 StudentMapper.java 文件

清单 8. StudentMapper.java 类

public interface StudentMapper { int insert(Student student); int updateIgnoreNullById(@Param(“student”) Student student); Student selectById(@Param(“id”) int id); int deleteById(@Param(“id”) int id); }

在 StudentMapper.java 类中我们定义了新增,更新(忽略空值),根据 id 查询学生,根据 id 删除学生四个方法,覆盖了简单的 CURD (Create、Update、Retrieve、Deletev操作,但是这四个只是方法的定义,那么具体实现呢?与我们平常所接触到的 interface 不一样的是,它的实现不是一个具体的 Java 类,而是一个与之对应的 mapper.xml文件,也就是我们接下来要看到的 StudentMapper.xml 文件。StudentMapper.java 类中定义的接口会对应 StudentMapper.xml 中的一段 SQL 语句。

创建 StudentMapper.xml 文件

创建一个与 StudentMapper.java 类对应的 XML 文件,里面定义了 StudentMapper.java 类中定义的 4 个方法的 SQL 实现,具体代码如下所示,篇幅的原因,完整的 XML 内容可以查看本文源码。

清单 9. StudentMapper.xml 文件

… insert into t_student(name,age,clazz_id,number) values (#{name},#{age},#{clazzId},#{number}) update t_student name = #{student.name} … where id = #{student.id} select * from t_student where id = #{id} …

其中 namespace 指定了该 XML 文件对应的 java 类。您可能还发现除了四个方法的定义外,还有一个 resultMap 的标签,这个其实定义的是我们 sql 查询的字段与实体类之间的映射关系。在 insert 方法中,我们使用了 useGeneratedKeys 和 keyProperty 两个属性,这两个属性的作用主要是将插入后数据的 id,赋值到传进来的实体对象的某个字段,keyProperty 就是指定那个字段的名称。

创建 ClazzMapper.java 文件

在 StudentMapper.java 中我们定义了简单的增删改查,在下面的代码中我们定义了一个需要多表关联才能实现的方法 ,selectWithStudentsById 方法会根据班级 id 查询出来该班级的信息以及该班级里的所有学生信息,我们定义了一个 ClazzExtend 类来接收查询结果,ClazzExtend 类扩展了 Clazz,它包含了一个学生集合属性,用来描述班级里的所有学生。

清单 10. ClazzMapper.java 类

public interface ClazzMapper { ClazzExtend selectWithStudentsById(int id); }

创建 ClazzMapper.xml 文件

清单 11. ClazzMapper.xml 文件

select c.id as id, c.name as name, s.id as sId, s.name as sName, s.age as sAge, s.clazz_id as sClazzId, s.number as sNumber from t_clazz as c left join t_student as s on s.clazz_id = c.id where c.id = #{id}

注意 resultMap 和 StudentMapper.xml 中的 resultMap 有些不一样,多了一个 collection 标签,这个标签是用来描述实体类中的集合属性的,MyBatis 会自动将我们 left join 后产生的多条数据合成一个数组并返回,是不是很方便?

测试代码

这里举例 ClazzMapper. selectWithStudentsById 方法的查询测试,其他方法的测试代码类似这里就不列举了,您可以尝试一下自己编写。

清单 12.测试方法代码

@RunWith(SpringRunner.class) @SpringBootTest public class XmlTest { @Autowired private ClazzMapper clazzMapper; @Test public void testSelectWithStudentsById() { ClazzExtend result = clazzMapper.selectWithStudentsById(1); System.out.println(JSON.toJSONString(result)); } }

执行以上单元测试后,会发现在控制台输出的结果中既包含班级信息也包含班级下面的学生信息,控制台输出的内容如下所示:

图 1. Mapper 方法测试结果

img

基于注解方式使用 MyBatis

虽然 MyBatis 设计之初是一个 XML 驱动的 ORM 框架,其配置信息都是基于 XML 的,但是从 MyBatis3 开始它基于强大的 Java 语言的配置 API 之上,支持使用注解来配置 SQL 以及查询结果与实体之间的映射关系。那么我们下面就来了解一下如何使用注解来使用 MyBatis。

注意本章节中所使用的表结构和例子与基于 XML 方式使用章节一致。话不多说,我们直接开始。Mapper.java 文件还是需要放在数据源指定的位置,我这里就不重复创建 Mapper 文件了,基于注解配置的代码也直接写在上面创建的 Mapper 文件中。

新增语句

清单 13. 基于注解的插入数据代码

@Insert("insert into t_student(name,age,clazz_id,number) values " + “(#{name},#{age},#{clazzId},#{number})”) @Options(useGeneratedKeys=true, keyProperty=“id”, keyColumn=“id”) int annoInsert(Student student);

查询语句

清单 14. 基于注解的查询数据代码

@Select(“select * from t_student where id = #{id}”) Student annoSelectById(Integer id);

您可能会奇怪,这里没有定义数据库字段和实体类之间的映射关系,MyBatis 是如何将查询结果转换成对象的呢?其实 MyBatis 会默认以下划线转驼峰的方式建立一个映射关系来转换。到这里您可能会问了,在基于注解的方式下如果字段名称和实体类属性名称不符合这个映射规则的话又该如何呢?基于注解配置映射关系将会在多表关联查询小节中讲解。

更新语句

清单 15. 基于注解的更新数据代码

@UpdateProvider(type = StudentDAOProvider.class, method = “updateIgnoreNullByPrimaryKey”) int annoUpdateIgnoreNullById(@Param(“student”) Student student); int annoUpdateIgnoreNullById(@Param(“student”) Student student);

您应该会发现 Update 方法和前面有些不同,我们指定了一个 type 和一个 method 属性,这是为什么呢?因为我们在更新的时候需要去判断该字段是否为空来决定是否更新该字段的内容,在 XML 的配置方式中我们可以通过 标签实现。那么通过注解呢?我们只能通过一个 Provider 类来动态生成 SQL 语句,下面的内容就是 StudentDAOProvider.class 的内容:

清单 16. StudentDAOProvider 的内容

public class StudentDAOProvider { public String updateIgnoreNullByPrimaryKey (Map<String, Object> map) throws Exception { Student student = (Student) map.get(“student”); if (student == null || student.getId() == null) { throw new Exception(“the primaryKey can not be null.”); } // 拼装 sql 语句 StringBuilder updateStrSb = new StringBuilder(“update t_student set “); StringBuilder setStrSb = new StringBuilder(); if (student.getName() != null) { setStrSb.append(“name = #{student.name},”); } if (student.getNumber() != null) { setStrSb.append(“number = #{student.number},”); } if (student.getAge() != null) { setStrSb.append(“age = #{student.age},”); } if (student.getClazzId() != null) { setStrSb.append(“clazz_id = #{student.clazzId},”); } if (setStrSb.length() > 0) { updateStrSb.append(setStrSb.substring(0, setStrSb. length()-1)).append(” where id = #{student.id}”); } else { throw new Exception(“none element to update.”); } return updateStrSb.toString(); } }

删除语句

清单 17. 基于注解的删除数据代码

@Delete(“delete from t_student where id = #{id}”) int annoDeleteById(Integer id);

多表关联查询

多表关联查询的例子和基于 XML 的例子相同,我们也是根据班级 id 查询班级信息以及该班级下所有的学生信息。基于注解的一对多的关联查询方式会比较繁琐,下面是具体的实现步骤。

首先在 StudentMapper.java 中定义一个根据班级 id 查询学生集合的方法。

清单 18. 根据班级 id 查询学生的代码

@Select(“select * from t_student where clazz_id = #{clazzId}”) List findByClazzId(Integer clazzId);

然后再在 ClazzMapper.java 中定义复杂查询的方法,如下所示:

清单 19. 多表关联查询的代码

@Select(“select * from t_clazz where id = #{id}”) @Results(id = “link”, value = { @Result(column = “id”, property = “id”), @Result(column = “name”, property = “name”), @Result(property = “students”, column = “id”, javaType = List.class, many = @Many(fetchType = FetchType.EAGER, select = “cn.itweknow.sbmybatis.mapper.StudentMapper. findByClazzId”))}) ClazzExtend annoSelectWithStudentsById(Integer id);

可以看到我们在基于注解的方式下可以通过 @Result 来制定查询结果与实体之间的映射关系。你可能还注意到查询班级中所有学生的属性实际上是指定了 StudentMapper 中的 findByClazzId 的方法来实现的,many=@Many 是指的这是一个一对多的查询,如果是一对一的查询的话可以使用 one=@One。select 则指定了这个一对多查询的具体方法,可能看到这里您会有一个疑问:这个复杂查询时使用的关联表查询还是分成了两个查询语句查询来得到结果的呢?那我们就一起来看一下吧:我们修改一下日志级别为 debug,这样可以将 MyBatis 实际执行的 SQL 语句打印出来,如下图所示。

图 2. 基于注解复杂查询的 SQL 打印

img

很明显可以看出来,是分成了两个 SQL 来查询的,这样如果关联表很多的话可能会导致查询效率比较低,可见复杂 SQL 的查询还是 XML 的方式好使一些。

XML 与注解的优劣对比

上面分别介绍了基于 XML 和注解两种方式来使用 MyBatis,那么到底使用哪种方式好呢?其实这个也需要看实际使用场景,比如说注解适合使用在一些简单查询的场景,而 XML 则在复杂查询的时候表现更佳。下面分别列举一下使用 XML 和注解的优劣之处供大家参考。

XML 的优劣点

优点:

排版能力强,特别是复杂 SQL 的排版,看起来更加清晰明了。

动态 SQL,在编写动态 SQL 的时候不得不说 XML 确实比注解要强,注解还需要单独定义一个 Provider 类来提供生成的 SQL。

缺点:

需要将 Mapper.xml 与 Mapper.java 类准确对应,容易出错。

查找一个接口对应的 SQL 语句不方便,还需要先找到对应的 XML 文件。

注解的优劣点

优点:

SQL 查找方便,SQL 直接放在接口上方的注解上,可以很容易找到。

缺点:

SQL 排版效果不好,复杂的 SQL 很难看明白其查询逻辑。

对多表关联查询的支持度不好。

对动态 SQL 的支持不好,还需要单独提供生成 SQL 的 Java 方法。

多表关联查询时,实际上是分成了多条 SQL 查询,如果关联比较多时可能会造成查询的效率比较低。

MyBatis 分页查询

在实际的开发过程中,我们可能会经常遇到分页查询的场景。如果直接用 SQL 语句的话,MySQL 可以通过 Limit 实现,MSSQL 可以通过 TOP 或者 row_number() 实现。那么 MyBatis 有没有对分页做支持呢?没有。我们可以通过 PageHelper 这个插件来实现这个需求。

添加依赖

首先添加 PagerHelper 相关的依赖包,在 pom.xml 中添加如下内容即可:

清单 20. 添加 PageHelper 依赖

com.github.pagehelper pagehelper 5.1.10

使用方式

首先在 Spring Boot 的配置文件中添加一些配置项:

清单 21. PageHelper 配置项

pagehelper: helperDialect: mysql reasonable: true

虽然说不同的数据库之间的 SQL 大体相同,但每种数据库都会有自己的方言,比如说不同的数据实现分页使用的关键字就不同,我们可以通过以配置 helperDialect 属性来指定分页插件使用哪种方言。Reasonable 是配置是否使用分页合理化参数,默认值为 false。当该参数设置为 true 时,pageNum<=0 时会查询第一页,pageNum>pages(超过总数时),会查询最后一页。默认 false 时,直接根据参数进行查询。

为了能够体现分页插件的效果,在使用之前先往 t_student 表中插入 15 条数据,然后在 Mapper 层定义一个查询所有学生的方法如下所示。

清单 22. 查询所有学生

@Select(“select *from t_student”) List findAll();

注意为了方便贴代码,这个方法的实现我使用的是注解的方式,如果您使用 XML 的配置方式也是完全可以的。

然后通过如下的代码就可以成功的实现分页查询学生列表了。

清单 23. 分页插件的使用

@Test public void testSelectPage() { //获取第 1 页,10 条内容,默认查询总数 count PageHelper.startPage(1, 10); //紧跟着的第一个 select 方法会被分页 List list = studentMapper.findAll(); PageInfo page = new PageInfo(list); System.out.println(JSON.toJSONString(page)); }

可以看到实现起来很方便,实际上 PageHelper 在调用 startPage 方法的时候会将分页信息保存在 ThreadLocal 中,然后对紧跟在 startPage 之后的一个查询语句应用分页,PageHelper 会去修改你的查询语句以达到分页的效果。PageInfo 里面包含了非常全面的分页属性如总数据条数、页大小、当前页数据等等。

图 3. PageInfo 对象的分页属性

img

在本教程中,您了解了如何在 Spring Boot 项目中整合 MyBatis,更深一步介绍了如何通过 XML 和注解两种方式使用 MyBatis 以及两种方式的优缺点,最后还了解了如何通过 PageHelper 来实现分页查询。您可以在 GitHub 上找到本教程的完整实现,如果您想对本教程做补充的话欢迎发邮件(gancy.programmer@gmail.com)给我或者直接在 GitHub 上提交 Pull Request。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/61794.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构—List集合

文章目录一、ArrayList集合1&#xff0c;ArrayList集合的特点2&#xff0c;ArrayList集合的一些方法添加元素个数获取指定位置元素指定位置插入元素替换元素移除元素判断是否为空判断是否包含元素移除指定元素迭代器遍历集合二、LinkedList集合1&#xff0c;LinkedList集合的特…

Ra-01SC/Ra-01SCH模组驱动

目录简介硬件准备设备清单接线说明重要函数说明调试联系我们简介 本文档主要介绍Ra-01SC/Ra-01SCH模组驱动例程使用方法&#xff1b; 硬件准备 设备清单 硬件数量Ra-001SC/Ra-01SCH 模组2个杜邦线若干USB转TTL模块2个STM32F103C8T6开发板2个安卓线2根 接线说明 STM32F103C…

[论文阅读] 颜色迁移-直方图渐进式颜色迁移

[论文阅读] 颜色迁移-直方图渐进式颜色迁移 文章: 有2篇相关的文章, 都为同一作者 [Progressive histogram reshaping for creative color transfer and tone reproduction], [code未公开][Progressive color transfer for images of arbitrary dynamic range], [code未公开]…

qt模块feature QT_FEATURE_* qt_lib_*.pri QT_CONFG qtConfig

qt为方便对代码进行删减&#xff0c;将代码拆分成很多模块&#xff0c;对每个模块梳理出feature&#xff0c;为了简化程序的使用和其他目的&#xff0c;qt还对每个模块分成public模块和private模块&#xff08;比如core分为core和core-privare模块&#xff0c;但两者公用同一个…

迭代器失效问题

目录 一、vector迭代器失效问题 1、resize,reserve,insert,assign,push_back可能引起底层空间改变 2、指定位置元素的删除操作erase 3、Linux下&#xff0c;g编译器对迭代器失效的检测不是非常严格&#xff0c;处理也没有vs下极端 4、与vector类似&#xff0c;string在插入…

java swing(GUI) MySQL实现的飞机票预定系统源码带视频运行教程

大家好&#xff0c;今天给大家演示一下由Java swing实现的飞机票预定系统&#xff0c;系统数据库原本采用的是Oracle&#xff0c;我又改了一个mysql版本的&#xff0c;所以这套系统有两个版本&#xff0c;一个是mysql数据库版的&#xff0c;一个是Oracle数据库版&#xff0c;演…

TypeScript是强类型,静态类型的Java Script

1. 编程语言的分类 As we all know, JavaScript 是弱类型&#xff0c;动态类型的编程语言。 首先我们来解释一下这几个名词&#xff1a; 动态类型语言&#xff1a;在 运行期间(Runtime) 才去做数据类型检查的语言。静态类型语言&#xff1a;在 编译其间(Compile) 就进行数据…

【Redis】事务秒杀案例

一、背景 在日常购物时&#xff0c;经常会有商家开展限时秒杀活动&#xff0c;我们如何使用redis来实现这种场景呢 二、业务代码 首先我们可以想到的是&#xff0c;我们可以把商品剩余数量和成功秒杀商品的用户id放在redis中 下面是我们的业务代码 package com.decade.con…

2022物联卡平台排名前十的公司

2022年物联网行业开始爆发&#xff0c;针对于企业设备联网的物联卡就显得格外重要了&#xff0c;而共享单车&#xff0c;移动支付&#xff0c;智慧城市&#xff0c;自动售卖机等企业采购物联卡会面临着各种问题&#xff0c;低价陷阱&#xff0c;流量虚假&#xff0c;管理混乱&a…

【Spring框架】经典的 9 种设计模式,面试工程师必学知识

文章目录1.简单工厂(非23种设计模式中的一种)实现方式&#xff1a;实质&#xff1a;实现原理&#xff1a;设计意义&#xff1a;2.工厂方法实现方式&#xff1a;实现原理&#xff1a;例子&#xff1a;3.单例模式4.适配器模式实现方式&#xff1a;实现原理&#xff1a;实现过程&a…

[Swift]国际化

一、添加本地化语言 比如这里&#xff0c;我们添加了联合国六种工作语言&#xff08;汉语&#xff0c;英语&#xff0c;法语&#xff0c;俄语&#xff0c;阿拉伯语和西班牙语&#xff09;。 二、纯代码本地化 1. 创建本地化文件 默认文件名为“Localizable”&#xff0c;不要…

【教程】如何在服务器上部署豆瓣小组抢沙发聊天机器人

由于在自己的电脑上运行软件比较麻烦&#xff0c;毕竟自己电脑还要用呢。所以这里选择吧软件放到服务器上去运行。 1、选择性价比最高的轻量应用服务器&#xff1a;https://url.cn/pXUtW9f8 2、一定要选择windows server&#xff01;&#xff01;&#xff01; 3、等待系统初始…

静态和默认路由配置-----计算机网络

拓扑图 实验场景&#xff1a;公司有一个总部和两个分支机构&#xff0c;其中AR1为总部路由器&#xff0c;其他两个为分支机构&#xff0c;ip网段如上图所示&#xff0c;现在通过配置路由器让三个地区可以互相通信。因为网络规模不大&#xff0c;所以采用静态路由和默认路由的方…

Postman进阶篇(十一)-在脚本中使用pm对象访问接口请求(pm.request.*)

在之前的文章中介绍过postman中的两个脚本——pre-request script或test script&#xff0c;在这两个脚本中都有使用到pm对象。&#xff08;pre-request script详细介绍、Test script详细介绍&#xff09;pm对象是在postman的脚本中非常重要&#xff0c;也是十分常用的方法。本…

文华财经期货多空趋势指标公式,期货幅图高抛低吸逃顶抄底精准买卖点信号系统

刚开始接触交易时&#xff0c;看着满屏的K线图&#xff0c;各种的 指标&#xff0c;脑子里自然会认为交易时一个非常复杂的事情&#xff0c;复杂到处处透露着神秘感&#xff0c;随着对交易学习的不断深入&#xff0c;看着厚厚的交易书籍&#xff0c;还 有复杂的图形演变、复杂的…

[附源码]Python计算机毕业设计SSM景区在线购票系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

HTML5期末大作业:美妆网页主题网站设计——清新的手工肥皂网站展示(4页)HTML+CSS+JavaScript

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

[附源码]计算机毕业设计共享汽车系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

昨晚停网后,我写了一段Python代码攻破了隔壁老王家的wifi密码

前言 本文给大家分享的是如何通过 Python 脚本实现 WIFI 密码的暴力攻防&#xff0c;从而实现免费蹭网。 开发工具 Python版本&#xff1a; 3.8 相关模块&#xff1a; pywifi模块 环境搭建 安装Python并添加到环境变量&#xff0c;pip安装需要的相关模块即可。 文中密码本…

前端问题解决方法

src动态绑定的时候&#xff0c;千万不要忘记了 : ​ display&#xff1a;inline-block导致高度缩小&#xff0c;而且height增大也没有任何变化display&#xff1a;inline-block其他问题&#xff0c;参考这篇博客前端 - 解决inline-block元素的3个 bug_个人文章 - SegmentFault …