首页
壁纸
留言板
友链
更多
统计归档
Search
1
TensorBoard:训练日志及网络结构可视化工具
12,687 阅读
2
主板开机跳线接线图【F_PANEL接线图】
9,825 阅读
3
移动光猫获取超级密码&开启公网ipv6
8,483 阅读
4
Linux使用V2Ray 原生客户端
7,530 阅读
5
NVIDIA 显卡限制功率
3,492 阅读
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
k8s
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
登录
/
注册
Search
标签搜索
好物分享
学习笔记
linux
MySQL
nvidia
typero
内网穿透
webdav
vps
java
cudann
gcc
cuda
树莓派
CNN
图像去雾
ssh安全
nps
暗通道先验
阿里云
jupiter
累计撰写
359
篇文章
累计收到
119
条评论
首页
栏目
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
k8s
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
页面
壁纸
留言板
友链
统计归档
搜索到
359
篇与
的结果
2022-03-11
Linux配置临时IP地址
偶然遇到了需要为某个网卡添加一个临时IP的问题1.添加临时IP ifconfig enp3s0:0 10.1.9.41 netmask 255.255.255.0 # 或者 ip addr add 10.1.9.41/24 dev enp3s0:02.删除临时IP ip addr delete 10.1.9.41 dev enp3s0:0参考资料ifconfig-网络管理命令ifconfig增加一个临时IP方法及删除方法
2022年03月11日
784 阅读
0 评论
0 点赞
2022-03-08
java实现大数的十六进制转八进制
1.问题描述问题描述 给定n个十六进制正整数,输出它们对应的八进制数。输入格式 输入的第一行为一个正整数n (1<=n<=10)。 接下来n行,每行一个由0-9、大写字母A-F组成的字符串,表示要转换的十六进制正整数,每个十六进制数长度不超过100000。输出格式 输出n行,每行为输入对应的八进制正整数。注意 输入的十六进制数不会有前导0,比如012A。 输出的八进制数也不能有前导0。样例输入 2 39 123ABC样例输出 71 4435274提示 先将十六进制数转换成某进制数,再由某进制数转换成八进制。2.代码实现import java.util.Scanner; public class Main { //16进制转2进制 public static String hex2Bin(String hex){ StringBuilder sb = new StringBuilder(); char[] hex_chars = hex.toCharArray(); for(char item:hex_chars){ switch (item){ case '0':sb.append("0000");break; case '1':sb.append("0001");break; case '2':sb.append("0010");break; case '3':sb.append("0011");break; case '4':sb.append("0100");break; case '5':sb.append("0101");break; case '6':sb.append("0110");break; case '7':sb.append("0111");break; case '8':sb.append("1000");break; case '9':sb.append("1001");break; case 'A':sb.append("1010");break; case 'B':sb.append("1011");break; case 'C':sb.append("1100");break; case 'D':sb.append("1101");break; case 'E':sb.append("1110");break; case 'F':sb.append("1111");break; default:sb.append("ERROR");break; } } return sb.toString(); } //2进制转8进制 public static String bin2Otc(String bin){ StringBuilder sb = new StringBuilder(); //将字符串的长度补齐到3的整数倍 int binLength = bin.length(); if(binLength%3==1){ bin = "00"+bin; }else if(binLength%3==2){ bin = "0"+bin; } //每3位转换出一个8进制数 for(int i=0;i<bin.length()/3;i++){ String octItem = bin.substring(i*3,i*3+3); switch (octItem){ case "000":sb.append("0");break; case "001":sb.append("1");break; case "010":sb.append("2");break; case "011":sb.append("3");break; case "100":sb.append("4");break; case "101":sb.append("5");break; case "110":sb.append("6");break; case "111":sb.append("7");break; default:sb.append("ERROR");break; } } return sb.toString(); } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int num_count = scanner.nextInt(); String []nums = new String[num_count]; for(int i=0;i<num_count;i++){ nums[i] = scanner.next(); } for(int i=0;i<num_count;i++){ String bin = hex2Bin(nums[i]); String oct = bin2Otc(bin); if(oct.charAt(0)=='0'){ oct = oct.substring(1); } System.out.println(oct); } } }参考资料1.http://lx.lanqiao.cn/problem.page?gpid=T51
2022年03月08日
615 阅读
0 评论
0 点赞
2022-03-06
MyBatis学习笔记
1.简介1.1 什么是MyBatisMyBatis 是一款优秀的持久层框架MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain Old Java Objects,普通的 Java对象】映射成数据库中的记录。MyBatis 本是apache的一个开源项目ibatis, 2010年这个项目由apache 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github .Mybatis官方文档 : http://www.mybatis.org/mybatis-3/zh/index.htmlGitHub : https://github.com/mybatis/mybatis-31.2 持久化持久化是将程序数据在持久状态和瞬时状态间转换的机制。即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。JDBC就是一种持久化机制。文件IO也是一种持久化机制。在生活中 : 将鲜肉冷藏,吃的时候再解冻的方法也是。将水果做成罐头的方法也是。为什么需要持久化服务呢?那是由于内存本身的缺陷引起的内存断电后数据会丢失,但有一些对象是无论如何都不能丢失的,比如银行账号等,遗憾的是,人们还无法保证内存永不掉电。内存过于昂贵,与硬盘、光盘等外存相比,内存的价格要高2~3个数量级,而且维持成本也高,至少需要一直供电吧。所以即使对象不需要永久保存,也会因为内存的容量限制不能一直呆在内存中,需要持久化来缓存到外存。1.3 持久层什么是持久层?完成持久化工作的代码块 . ----> dao层 【DAO (Data Access Object) 数据访问对象】大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。不过这里有一个字需要特别强调,也就是所谓的“层”。对于应用系统而言,数据持久功能大多是必不可少的组成部分。也就是说,我们的系统中,已经天然的具备了“持久层”概念?也许是,但也许实际情况并非如此。之所以要独立出一个“持久层”的概念,而不是“持久模块”,“持久单元”,也就意味着,我们的系统架构中,应该有一个相对独立的逻辑层面,专注于数据持久化逻辑的实现.与系统其他部分相对而言,这个层面应该具有一个较为清晰和严格的逻辑边界。【说白了就是用来操作数据库存在的!】1.4 为什么需要MybatisMybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据 .传统的jdbc操作 , 有很多重复代码块 .比如 : 数据取出时的封装 , 数据库的建立连接等等... , 通过框架可以减少重复代码,提高开发效率 .MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射所有的事情,不用Mybatis依旧可以做到,只是用了它,所有实现会更加简单!技术没有高低之分,只有使用这个技术的人有高低之别MyBatis的优点简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件就可以了,易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。提供xml标签,支持编写动态sql。.......最重要的一点,使用的人多!公司需要!2.MyBatis第一个程序思路流程:搭建环境-->导入Mybatis--->编写代码--->测试2.1 搭建实验数据库CREATE DATABASE `mybatis`; USE `mybatis`; DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(20) NOT NULL, `name` varchar(30) DEFAULT NULL, `pwd` varchar(30) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into `user`(`id`,`name`,`pwd`) values (1,'狂神','123456'),(2,'张三','abcdef'),(3,'李四','987654');2.2 导入MyBatis相关 jar 包GitHub上找<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency>2.3 编写MyBatis核心配置文件查看帮助文档<?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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/kuang/dao/userMapper.xml"/> </mappers> </configuration>2.4 编写MyBatis工具类查看帮助文档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 { private static SqlSessionFactory sqlSessionFactory; static { try { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //获取SqlSession连接 public static SqlSession getSession(){ return sqlSessionFactory.openSession(); } }2.5 创建实体类public class User { private int id; //id private String name; //姓名 private String pwd; //密码 //构造,有参,无参 //set/get //toString() }2.6 编写Mapper接口类import com.kuang.pojo.User; import java.util.List; public interface UserMapper { List<User> selectUser(); }2.7 编写Mapper.xml配置文件namespace 十分重要,不能写错!<?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.kuang.dao.UserMapper"> <select id="selectUser" resultType="com.kuang.pojo.User"> select * from user </select> </mapper>2.8 编写测试类Junit 包测试public class MyTest { @Test public void selectUser() { SqlSession session = MybatisUtils.getSession(); //方法一: //List<User> users = session.selectList("com.kuang.mapper.UserMapper.selectUser"); //方法二: UserMapper mapper = session.getMapper(UserMapper.class); List<User> users = mapper.selectUser(); for (User user: users){ System.out.println(user); } session.close(); } }2.9 运行测试,成功的查询出来的我们的数据,ok!2.10 问题说明可能出现问题说明:Maven静态资源过滤问题<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>有了MyBatis以后再也不用写原生的JDBC代码了,舒服!3.CRUD操作3.1 selectselect标签是mybatis中最常用的标签之一select语句有很多属性可以详细配置每一条SQL语句SQL语句返回值类型。【完整的类名或者别名】传入SQL语句的参数类型 。【万能的Map,可以多尝试使用】命名空间中唯一的标识符接口中的方法名与映射文件中的SQL语句ID 一一对应idparameterTyperesultType需求:根据id查询用户1、在UserMapper中添加对应方法public interface UserMapper { //查询全部用户 List<User> selectUser(); //根据id查询用户 User selectUserById(int id); }2、在UserMapper.xml中添加Select语句<select id="selectUserById" resultType="com.kuang.pojo.User"> select * from user where id = #{id} </select>3、测试类中测试@Test public void tsetSelectUserById() { SqlSession session = MybatisUtils.getSession(); //获取SqlSession连接 UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); System.out.println(user); session.close(); }课堂练习:根据 密码 和 名字 查询用户思路一:直接在方法中传递参数1、在接口方法的参数前加 @Param属性2、Sql语句编写的时候,直接取@Param中设置的值即可,不需要单独设置参数类型//通过密码和名字查询用户 User selectUserByNP(@Param("username") String username,@Param("pwd") String pwd); /* <select id="selectUserByNP" resultType="com.kuang.pojo.User"> select * from user where name = #{username} and pwd = #{pwd} </select> */思路二:使用万能的Map1、在接口方法中,参数直接传递Map;User selectUserByNP2(Map<String,Object> map);2、编写sql语句的时候,需要传递参数类型,参数类型为map<select id="selectUserByNP2" parameterType="map" resultType="com.kuang.pojo.User"> select * from user where name = #{username} and pwd = #{pwd} </select>3、在使用方法的时候,Map的 key 为 sql中取的值即可,没有顺序要求!Map<String, Object> map = new HashMap<String, Object>(); map.put("username","小明"); map.put("pwd","123456"); User user = mapper.selectUserByNP2(map);总结:如果参数过多,我们可以考虑直接使用Map实现,如果参数比较少,直接传递参数即可3.2 insert我们一般使用insert标签进行插入操作,它的配置和select标签差不多!需求:给数据库增加一个用户1、在UserMapper接口中添加对应的方法//添加一个用户 int addUser(User user);2、在UserMapper.xml中添加insert语句<insert id="addUser" parameterType="com.kuang.pojo.User"> insert into user (id,name,pwd) values (#{id},#{name},#{pwd}) </insert>3、测试@Test public void testAddUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(5,"王五","zxcvbn"); int i = mapper.addUser(user); System.out.println(i); session.commit(); //提交事务,重点!不写的话不会提交到数据库 session.close(); }注意点:增、删、改操作需要提交事务!3.3update我们一般使用update标签进行更新操作,它的配置和select标签差不多!需求:修改用户的信息1、同理,编写接口方法//修改一个用户 int updateUser(User user);2、编写对应的配置文件SQL<update id="updateUser" parameterType="com.kuang.pojo.User"> update user set name=#{name},pwd=#{pwd} where id = #{id} </update>3、测试@Test public void testUpdateUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); user.setPwd("asdfgh"); int i = mapper.updateUser(user); System.out.println(i); session.commit(); //提交事务,重点!不写的话不会提交到数据库 session.close(); }3.4 delete我们一般使用delete标签进行删除操作,它的配置和select标签差不多!需求:根据id删除一个用户1、同理,编写接口方法//根据id删除用户 int deleteUser(int id);2、编写对应的配置文件SQL<delete id="deleteUser" parameterType="int"> delete from user where id = #{id} </delete>3、测试@Test public void testDeleteUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); int i = mapper.deleteUser(5); System.out.println(i); session.commit(); //提交事务,重点!不写的话不会提交到数据库 session.close(); }小结:所有的增删改操作都需要提交事务!接口所有的普通参数,尽量都写上@Param参数,尤其是多个参数时,必须写上!有时候根据业务的需求,可以考虑使用map传递参数!为了规范操作,在SQL的配置文件中,我们尽量将Parameter参数和resultType都写上!3.5 思考题模糊查询like语句该怎么写?第1种:在Java代码中添加sql通配符。string wildcardname = “%smi%”; list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”> select * from foo where bar like #{value} </select>第2种:在sql语句中拼接通配符,会引起sql注入string wildcardname = “smi”; list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”> select * from foo where bar like "%"#{value}"%" </select>4.配置解析4.1 核心配置文件mybatis-config.xml 系统核心配置文件MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。能配置的内容如下:configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器) <!-- 注意元素节点的顺序!顺序不对会报错 -->我们可以阅读 mybatis-config.xml 上面的dtd的头文件!4.2 environments元素<environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>配置MyBatis的多套运行环境,将SQL映射到多个不同的数据库上,必须指定其中一个为默认运行环境(通过default指定)子元素节点:environmentdataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。数据源是必须配置的。有三种内建的数据源类型type="[UNPOOLED|POOLED|JNDI]")unpooled:这个数据源的实现只是每次被请求时打开和关闭连接。pooled:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 这是一种使得并发 Web 应用快速响应请求的流行处理方式。jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。数据源也有很多第三方的实现,比如dbcp,c3p0,druid等等....详情:点击查看官方文档这两种事务管理器类型都不需要设置任何属性。具体的一套环境,通过设置id进行区别,id保证唯一!子元素节点:transactionManager - [ 事务管理器 ]<!-- 语法 --> <transactionManager type="[ JDBC | MANAGED ]"/>子元素节点:数据源(dataSource)4.3 mappers元素mappers映射器 : 定义映射SQL语句文件既然 MyBatis 的行为其他元素已经配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。映射器是MyBatis中最核心的组件之一,在MyBatis 3之前,只支持xml映射器,即:所有的SQL语句都必须在xml文件中配置。而从MyBatis 3开始,还支持接口映射器,这种映射器方式允许以Java代码的方式注解定义SQL语句,非常简洁。引入资源方式<!-- 使用相对于类路径的资源引用 --> <mappers> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers> <!-- 使用完全限定资源定位符(URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> </mappers> <!-- 使用映射器接口实现类的完全限定类名 需要配置文件名称和接口名称一致,并且位于同一目录下 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> </mappers> <!-- 将包内的映射器接口实现全部注册为映射器 但是需要配置文件名称和接口名称一致,并且位于同一目录下 --> <mappers> <package name="org.mybatis.builder"/> </mappers>Mapper文件<?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.kuang.mapper.UserMapper"> </mapper>namespace中文意思:命名空间,作用如下:namespace的命名必须跟某个接口同名接口中的方法与映射文件中sql语句id应该一一对应namespace和子元素的id联合保证唯一 , 区别不同的mapper绑定DAO接口namespace命名规则 : 包名+类名MyBatis 的真正强大在于它的映射语句,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 为聚焦于 SQL 而构建,以尽可能地为你减少麻烦。4.4 Properties优化数据库这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。具体的官方文档我们来优化我们的配置文件第一步 ; 在资源目录下新建一个db.propertiesdriver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8 username=root password=123456第二步 : 将文件导入properties 配置文件<configuration> <!--导入properties文件--> <properties resource="db.properties"/> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers> </configuration>更多操作,可以查看官方文档!【演示带领学习】配置文件优先级问题新特性:使用占位符4.5 typeAliases优化类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。<!--配置别名,注意顺序--> <typeAliases> <typeAlias type="com.kuang.pojo.User" alias="User"/> </typeAliases>当这样配置时,User可以用在任何使用com.kuang.pojo.User的地方。也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:<typeAliases> <package name="com.kuang.pojo"/> </typeAliases>每一个在包 com.kuang.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。若有注解,则别名为其注解值。见下面的例子:@Alias("user") public class User { ... }去官网查看一下Mybatis默认的一些类型别名!4.6 其他配置浏览设置设置(settings)相关 => 查看帮助文档懒加载日志实现缓存开启关闭一个配置完整的 settings 元素的示例如下:<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25"/> <setting name="defaultFetchSize" value="100"/> <setting name="safeRowBoundsEnabled" value="false"/> <setting name="mapUnderscoreToCamelCase" value="false"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="OTHER"/> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings>类型处理器无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。【了解即可】对象工厂MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过有参构造方法来实例化。如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。【了解即可】4.7 生命周期和作用域作用域(Scope)和生命周期理解我们目前已经讨论过的不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。我们可以先画一个流程图,分析一下Mybatis的执行过程!作用域理解SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try...catch...finally... 语句来保证其正确关闭。所以 SqlSession 的最佳的作用域是请求或方法作用域。5.ResultMap5.1 查询为null问题要解决的问题:属性名和字段名不一致环境:新建一个项目,将之前的项目拷贝过来1、查看之前的数据库的字段名2、Java中的实体类设计public class User { private int id; //id private String name; //姓名 private String password; //密码和数据库不一样! //构造 //set/get //toString() }3、接口//根据id查询用户 User selectUserById(int id);4、mapper映射文件<select id="selectUserById" resultType="user"> select * from user where id = #{id} </select>5、测试@Test public void testSelectUserById() { SqlSession session = MybatisUtils.getSession(); //获取SqlSession连接 UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); System.out.println(user); session.close(); }结果:User{id=1, name='狂神', password='null'}查询出来发现 password 为空 . 说明出现了问题!分析:select * from user where id = #{id} 可以看做select id,name,pwd from user where id = #{id}mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找相应列名的set方法设值 , 由于找不到setPwd() , 所以password返回null ; 【自动映射】5.2 解决方案方案一:为列名指定别名 , 别名和java实体类的属性名一致 .<select id="selectUserById" resultType="User"> select id , name , pwd as password from user where id = #{id} </select>方案二:使用结果集映射->ResultMap 【推荐】<resultMap id="UserMap" type="User"> <!-- id为主键 --> <id column="id" property="id"/> <!-- column是数据库表的列名 , property是对应实体类的属性名 --> <result column="name" property="name"/> <result column="pwd" property="password"/> </resultMap> <select id="selectUserById" resultMap="UserMap"> select id , name , pwd from user where id = #{id} </select>5.3 ResultMap自动映射resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的长达数千行的代码。ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。你已经见过简单映射语句的示例了,但并没有显式指定 resultMap。比如:<select id="selectUserById" resultType="map"> select id , name , pwd from user where id = #{id} </select>上述语句只是简单地将所有的列映射到 HashMap 的键上,这由 resultType 属性指定。虽然在大部分情况下都够用,但是 HashMap 不是一个很好的模型。你的程序更可能会使用 JavaBean 或 POJO(Plain Old Java Objects,普通老式 Java 对象)作为模型。ResultMap 最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。手动映射1、返回值类型为resultMap<select id="selectUserById" resultMap="UserMap"> select id , name , pwd from user where id = #{id} </select>2、编写resultMap,实现手动映射!<resultMap id="UserMap" type="User"> <!-- id为主键 --> <id column="id" property="id"/> <!-- column是数据库表的列名 , property是对应实体类的属性名 --> <result column="name" property="name"/> <result column="pwd" property="password"/> </resultMap>如果世界总是这么简单就好了。但是肯定不是的,数据库中,存在一对多,多对一的情况,我们之后会使用到一些高级的结果集映射,association,collection这些,我们将在之后讲解,今天你们需要把这些知识都消化掉才是最重要的!理解结果集映射的这个概念!6.日志6.1 日志工厂思考:我们在测试SQL的时候,要是能够在控制台输出 SQL 的话,是不是就能够有更快的排错效率?如果一个 数据库相关的操作出现了问题,我们可以根据输出的SQL语句快速排查问题。对于以往的开发过程,我们会经常使用到debug模式来调节,跟踪我们的代码执行过程。但是现在使用Mybatis是基于接口,配置文件的源代码执行过程。因此,我们必须选择日志工具来作为我们开发,调节程序的工具。Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:SLF4JApache Commons LoggingLog4j 2Log4jJDK logging具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。标准日志实现指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>测试,可以看到控制台有大量的输出!我们可以通过这些输出来判断程序到底哪里出了Bug6.2 Log4j简介:Log4j是Apache的一个开源项目通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件....我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。使用步骤:1、导入log4j的包<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>2、配置文件编写,在resources下新建log4j.properties#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/kuang.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG3、setting设置日志实现<settings> <setting name="logImpl" value="LOG4J"/> </settings>4、在程序中使用Log4j进行输出!//注意导包:org.apache.log4j.Logger static Logger logger = Logger.getLogger(MyTest.class); @Test public void selectUser() { logger.info("info:进入selectUser方法"); logger.debug("debug:进入selectUser方法"); logger.error("error: 进入selectUser方法"); SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); List<User> users = mapper.selectUser(); for (User user: users){ System.out.println(user); } session.close(); }5、测试,看控制台输出!使用Log4j 输出日志可以看到还生成了一个日志的文件 【需要修改file的日志级别】7.分页7.1 limit实现分页思考:为什么需要分页?在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。使用Limit实现分页#语法 SELECT * FROM table LIMIT stratIndex,pageSize SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15 #为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1: SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last. #如果只给定一个参数,它表示返回最大的记录行数目: SELECT * FROM table LIMIT 5; //检索前 5 个记录行 #换句话说,LIMIT n 等价于 LIMIT 0,n。 步骤:1、修改Mapper文件<select id="selectUser" parameterType="map" resultType="user"> select * from user limit #{startIndex},#{pageSize} </select>2、Mapper接口,参数为map//选择全部用户实现分页 List<User> selectUser(Map<String,Integer> map);3、在测试类中传入参数测试推断:起始位置 = (当前页面 - 1 ) * 页面大小//分页查询 , 两个参数startIndex , pageSize @Test public void testSelectUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); int currentPage = 1; //第几页 int pageSize = 2; //每页显示几个 Map<String,Integer> map = new HashMap<String,Integer>(); map.put("startIndex",(currentPage-1)*pageSize); map.put("pageSize",pageSize); List<User> users = mapper.selectUser(map); for (User user: users){ System.out.println(user); } session.close(); }7.2 RowBounds分页我们除了使用Limit在SQL层面实现分页,也可以使用RowBounds在Java代码层面实现分页,当然此种方式作为了解即可。我们来看下如何实现的!步骤:1、mapper接口//选择全部用户RowBounds实现分页 List<User> getUserByRowBounds();2、mapper文件<select id="getUserByRowBounds" resultType="user"> select * from user </select>3、测试类在这里,我们需要使用RowBounds类@Test public void testUserByRowBounds() { SqlSession session = MybatisUtils.getSession(); int currentPage = 2; //第几页 int pageSize = 2; //每页显示几个 RowBounds rowBounds = new RowBounds((currentPage-1)*pageSize,pageSize); //通过session.**方法进行传递rowBounds,[此种方式现在已经不推荐使用了] List<User> users = session.selectList("com.kuang.mapper.UserMapper.getUserByRowBounds", null, rowBounds); for (User user: users){ System.out.println(user); } session.close(); }7.3 PageHelper了解即可,可以自己尝试使用官方文档:https://pagehelper.github.io/在MyBatisPlus中,我们也讲解到了分页实现,所以实现方式很多,看自己的理解和熟练程度进行掌握即可!8.使用注解开发8.1 面向接口编程大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的实现 , 大家都遵守共同的标准 , 使得开发变得容易 , 规范性更好在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。关于接口的理解接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。接口的本身反映了系统设计人员对系统的抽象理解。接口应有两类:第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);一个体有可能有多个抽象面。抽象体与抽象面是有区别的。三个面向区别面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法 .面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现 .接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是对系统整体的架构8.2 利用注解开发mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建sql 类型主要分成 :@select ()@update ()@Insert ()@delete ()注意:利用注解开发就不需要mapper.xml映射文件了 .1、我们在我们的接口中添加注解//查询全部用户 @Select("select id,name,pwd password from user") public List<User> getAllUser();2、在mybatis的核心配置文件中注入<!--使用class绑定接口--> <mappers> <mapper class="com.kuang.mapper.UserMapper"/> </mappers>3、我们去进行测试@Test public void testGetAllUser() { SqlSession session = MybatisUtils.getSession(); //本质上利用了jvm的动态代理机制 UserMapper mapper = session.getMapper(UserMapper.class); List<User> users = mapper.getAllUser(); for (User user : users){ System.out.println(user); } session.close(); }4、利用Debug查看本质5、本质上利用了jvm的动态代理机制6、Mybatis详细的执行流程8.3 注解增删改改造MybatisUtils工具类的getSession( ) 方法,重载实现。 //获取SqlSession连接 public static SqlSession getSession(){ return getSession(true); //事务自动提交 } public static SqlSession getSession(boolean flag){ return sqlSessionFactory.openSession(flag); }【注意】确保实体类和数据库字段对应查询:1、编写接口方法注解//根据id查询用户 @Select("select * from user where id = #{id}") User selectUserById(@Param("id") int id);2、测试@Test public void testSelectUserById() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); System.out.println(user); session.close(); }新增:1、编写接口方法注解//添加一个用户 @Insert("insert into user (id,name,pwd) values (#{id},#{name},#{pwd})") int addUser(User user);2、测试@Test public void testAddUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(6, "秦疆", "123456"); mapper.addUser(user); session.close(); }修改:1、编写接口方法注解//修改一个用户 @Update("update user set name=#{name},pwd=#{pwd} where id = #{id}") int updateUser(User user);2、测试@Test public void testUpdateUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(6, "秦疆", "zxcvbn"); mapper.updateUser(user); session.close(); }删除:1、编写接口方法注解//根据id删除用 @Delete("delete from user where id = #{id}") int deleteUser(@Param("id")int id);2、测试@Test public void testDeleteUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); mapper.deleteUser(6); session.close(); }【注意点:增删改一定记得对事务的处理】8.4 关于@Param@Param注解用于给方法参数起一个名字。以下是总结的使用原则:在方法只接受一个参数的情况下,可以不使用@Param。在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。如果参数是 JavaBean , 则不能使用@Param。不使用@Param注解时,参数只能有一个,并且是Javabean。8.5 #与$的区别\#{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符? 【推荐使用】INSERT INTO user (name) VALUES (#{name}); INSERT INTO user (name) VALUES (?);${} 的作用是直接进行字符串替换INSERT INTO user (name) VALUES ('${name}'); INSERT INTO user (name) VALUES ('kuangshen');使用注解和配置文件协同开发,才是MyBatis的最佳实践!9.多对一处理多对一的理解:多个学生对应一个老师如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师!9.1 数据库设计CREATE TABLE `teacher` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师'); CREATE TABLE `student` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, `tid` INT(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `fktid` (`tid`), CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');9.2 搭建测试环境1、IDEA安装Lombok插件2、引入Maven依赖<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency>3、在代码中增加注解@Data //GET,SET,ToString,有参,无参构造 public class Teacher { private int id; private String name; } @Data public class Student { private int id; private String name; //多个学生可以是同一个老师,即多对一 private Teacher teacher; }4、编写实体类对应的Mapper接口 【两个】无论有没有需求,都应该写上,以备后来之需!public interface StudentMapper { } public interface TeacherMapper { }5、编写Mapper接口对应的 mapper.xml配置文件 【两个】无论有没有需求,都应该写上,以备后来之需!<?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.kuang.mapper.StudentMapper"> </mapper> <?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.kuang.mapper.TeacherMapper"> </mapper>9.3 按查询嵌套处理1、给StudentMapper接口增加方法//获取所有学生及对应老师的信息 public List<Student> getStudents();2、编写对应的Mapper文件<?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.kuang.mapper.StudentMapper"> <!-- 需求:获取所有学生及对应老师的信息 思路: 1. 获取所有学生的信息 2. 根据获取的学生信息的老师ID->获取该老师的信息 3. 思考问题,这样学生的结果集中应该包含老师,该如何处理呢,数据库中我们一般使用关联查询? 1. 做一个结果集映射:StudentTeacher 2. StudentTeacher结果集的类型为 Student 3. 学生中老师的属性为teacher,对应数据库中为tid。 多个 [1,...)学生关联一个老师=> 一对一,一对多 4. 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询 --> <select id="getStudents" resultMap="StudentTeacher"> select * from student </select> <resultMap id="StudentTeacher" type="Student"> <!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名--> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <!-- 这里传递过来的id,只有一个属性的时候,下面可以写任何值 association中column多参数配置: column="{key=value,key=value}" 其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的字段名。 --> <select id="getTeacher" resultType="teacher"> select * from teacher where id = #{id} </select> </mapper>3、编写完毕去Mybatis配置文件中,注册Mapper!4、注意点说明:<resultMap id="StudentTeacher" type="Student"> <!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名--> <association property="teacher" column="{id=tid,name=tid}" javaType="Teacher" select="getTeacher"/> </resultMap> <!-- 这里传递过来的id,只有一个属性的时候,下面可以写任何值 association中column多参数配置: column="{key=value,key=value}" 其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的字段名。 --> <select id="getTeacher" resultType="teacher"> select * from teacher where id = #{id} and name = #{name} </select>5、测试@Test public void testGetStudents(){ SqlSession session = MybatisUtils.getSession(); StudentMapper mapper = session.getMapper(StudentMapper.class); List<Student> students = mapper.getStudents(); for (Student student : students){ System.out.println( "学生名:"+ student.getName() +"\t老师:"+student.getTeacher().getName()); } }9.4 按结果嵌套处理除了上面这种方式,还有其他思路吗?我们还可以按照结果进行嵌套处理;1、接口方法编写public List<Student> getStudents2();2、编写对应的mapper文件<!-- 按查询结果嵌套处理 思路: 1. 直接查询出结果,进行结果集的映射 --> <select id="getStudents2" resultMap="StudentTeacher2" > select s.id sid, s.name sname , t.name tname from student s,teacher t where s.tid = t.id </select> <resultMap id="StudentTeacher2" type="Student"> <id property="id" column="sid"/> <result property="name" column="sname"/> <!--关联对象property 关联对象在Student实体类中的属性--> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> </association> </resultMap>3、去mybatis-config文件中注入【此处应该处理过了】4、测试@Test public void testGetStudents2(){ SqlSession session = MybatisUtils.getSession(); StudentMapper mapper = session.getMapper(StudentMapper.class); List<Student> students = mapper.getStudents2(); for (Student student : students){ System.out.println( "学生名:"+ student.getName() +"\t老师:"+student.getTeacher().getName()); } }9.5 小结按照查询进行嵌套处理就像SQL中的子查询按照结果进行嵌套处理就像SQL中的联表查询10.一对多的处理一对多的理解:一个老师拥有多个学生如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)!10.1 实体类编写@Data public class Student { private int id; private String name; private int tid; } @Data public class Teacher { private int id; private String name; //一个老师多个学生 private List<Student> students; }..... 和之前一样,搭建测试的环境!10.2 按结果嵌套处理1、TeacherMapper接口编写方法//获取指定老师,及老师下的所有学生 public Teacher getTeacher(int id);2、编写接口对应的Mapper配置文件<mapper namespace="com.kuang.mapper.TeacherMapper"> <!-- 思路: 1. 从学生表和老师表中查出学生id,学生姓名,老师姓名 2. 对查询出来的操作做结果集映射 1. 集合的话,使用collection! JavaType和ofType都是用来指定对象类型的 JavaType是用来指定pojo中属性的类型 ofType指定的是映射到list集合属性中pojo的类型。 --> <select id="getTeacher" resultMap="TeacherStudent"> select s.id sid, s.name sname , t.name tname, t.id tid from student s,teacher t where s.tid = t.id and t.id=#{id} </select> <resultMap id="TeacherStudent" type="Teacher"> <result property="name" column="tname"/> <collection property="students" ofType="Student"> <result property="id" column="sid" /> <result property="name" column="sname" /> <result property="tid" column="tid" /> </collection> </resultMap> </mapper>3、将Mapper文件注册到MyBatis-config文件中<mappers> <mapper resource="mapper/TeacherMapper.xml"/> </mappers>4、测试@Test public void testGetTeacher(){ SqlSession session = MybatisUtils.getSession(); TeacherMapper mapper = session.getMapper(TeacherMapper.class); Teacher teacher = mapper.getTeacher(1); System.out.println(teacher.getName()); System.out.println(teacher.getStudents()); }10.3 按查询嵌套处理1、TeacherMapper接口编写方法public Teacher getTeacher2(int id);2、编写接口对应的Mapper配置文件<select id="getTeacher2" resultMap="TeacherStudent2"> select * from teacher where id = #{id} </select> <resultMap id="TeacherStudent2" type="Teacher"> <!--column是一对多的外键 , 写的是一的主键的列名--> <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId"/> </resultMap> <select id="getStudentByTeacherId" resultType="Student"> select * from student where tid = #{id} </select>3、将Mapper文件注册到MyBatis-config文件中4、测试@Test public void testGetTeacher2(){ SqlSession session = MybatisUtils.getSession(); TeacherMapper mapper = session.getMapper(TeacherMapper.class); Teacher teacher = mapper.getTeacher2(1); System.out.println(teacher.getName()); System.out.println(teacher.getStudents()); }小结1、关联-association2、集合-collection3、所以association是用于一对一和多对一,而collection是用于一对多的关系4、JavaType和ofType都是用来指定对象类型的JavaType是用来指定pojo中属性的类型ofType指定的是映射到list集合属性中pojo的类型。注意说明:1、保证SQL的可读性,尽量通俗易懂2、根据实际要求,尽量编写性能更高的SQL语句3、注意属性名和字段不一致的问题4、注意一对多和多对一 中:字段和属性对应的问题5、尽量使用Log4j,通过日志来查看自己的错误11.动态SQL11.1 介绍什么是动态SQL:动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句.官网描述: MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。 虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。 动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。 ------------------------------- - if - choose (when, otherwise) - trim (where, set) - foreach -------------------------------我们之前写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。那么怎么去解决这个问题呢?这就要使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。11.2 搭建环境新建一个数据库表:blog字段:id,title,author,create_time,viewsCREATE TABLE `blog` ( `id` varchar(50) NOT NULL COMMENT '博客id', `title` varchar(100) NOT NULL COMMENT '博客标题', `author` varchar(30) NOT NULL COMMENT '博客作者', `create_time` datetime NOT NULL COMMENT '创建时间', `views` int(30) NOT NULL COMMENT '浏览量' ) ENGINE=InnoDB DEFAULT CHARSET=utf81、创建Mybatis基础工程2、IDutil工具类public class IDUtil { public static String genId(){ return UUID.randomUUID().toString().replaceAll("-",""); } }3、实体类编写 【注意set方法作用】import java.util.Date; public class Blog { private String id; private String title; private String author; private Date createTime; private int views; //set,get.... }4、编写Mapper接口及xml文件public interface BlogMapper { } <?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.kuang.mapper.BlogMapper"> </mapper>5、mybatis核心配置文件,下划线驼峰自动转换<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <!--注册Mapper.xml--> <mappers> <mapper resource="mapper/BlogMapper.xml"/> </mappers>6、插入初始数据编写接口//新增一个博客 int addBlog(Blog blog);sql配置文件<insert id="addBlog" parameterType="blog"> insert into blog (id, title, author, create_time, views) values (#{id},#{title},#{author},#{createTime},#{views}); </insert>初始化博客方法@Test public void addInitBlog(){ SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = new Blog(); blog.setId(IDUtil.genId()); blog.setTitle("Mybatis如此简单"); blog.setAuthor("狂神说"); blog.setCreateTime(new Date()); blog.setViews(9999); mapper.addBlog(blog); blog.setId(IDUtil.genId()); blog.setTitle("Java如此简单"); mapper.addBlog(blog); blog.setId(IDUtil.genId()); blog.setTitle("Spring如此简单"); mapper.addBlog(blog); blog.setId(IDUtil.genId()); blog.setTitle("微服务如此简单"); mapper.addBlog(blog); session.close(); }初始化数据完毕!11.3 if 语句需求:根据作者名字和博客名字来查询博客!如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询1、编写接口类//需求1 List<Blog> queryBlogIf(Map map);2、编写SQL语句<!--需求1: 根据作者名字和博客名字来查询博客! 如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询 select * from blog where title = #{title} and author = #{author} --> <select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog where <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </select>3、测试@Test public void testQueryBlogIf(){ SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap<String, String> map = new HashMap<String, String>(); map.put("title","Mybatis如此简单"); map.put("author","狂神说"); List<Blog> blogs = mapper.queryBlogIf(map); System.out.println(blogs); session.close(); }这样写我们可以看到,如果 author 等于 null,那么查询语句为 select from user where title=#{title},但是如果title为空呢?那么查询语句为 select from user where and author=#{author},这是错误的 SQL 语句,如何解决呢?请看下面的 where 语句!11.4 Where修改上面的SQL语句;<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </where> </select>这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。11.5 Set同理,上面的对于查询 SQL 语句包含 where 关键字,如果在进行更新操作的时候,含有 set 关键词,我们怎么处理呢?1、编写接口方法int updateBlog(Map map);2、sql配置文件<!--注意set是用的逗号隔开--> <update id="updateBlog" parameterType="map"> update blog <set> <if test="title != null"> title = #{title}, </if> <if test="author != null"> author = #{author} </if> </set> where id = #{id}; </update>3、测试@Test public void testUpdateBlog(){ SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap<String, String> map = new HashMap<String, String>(); map.put("title","动态SQL"); map.put("author","秦疆"); map.put("id","9d6a763f5e1347cebda43e2a32687a77"); mapper.updateBlog(map); session.close(); }11.6 choose语句有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句1、编写接口方法List<Blog> queryBlogChoose(Map map);2、sql配置文件<select id="queryBlogChoose" parameterType="map" resultType="blog"> select * from blog <where> <choose> <when test="title != null"> title = #{title} </when> <when test="author != null"> and author = #{author} </when> <otherwise> and views = #{views} </otherwise> </choose> </where> </select>3、测试类@Test public void testQueryBlogChoose(){ SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap<String, Object> map = new HashMap<String, Object>(); map.put("title","Java如此简单"); map.put("author","狂神说"); map.put("views",9999); List<Blog> blogs = mapper.queryBlogChoose(map); System.out.println(blogs); session.close(); }11.7 SQL片段有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。提取SQL片段:<sql id="if-title-author"> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql>引用SQL片段:<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace --> <include refid="if-title-author"></include> <!-- 在这里还可以引用其他的 sql 片段 --> </where> </select>注意:①、最好基于 单表来定义 sql 片段,提高片段的可重用性②、在 sql 片段中不要包括 where11.8 Foreach将数据库中前三个数据的id修改为1,2,3;需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息1、编写接口List<Blog> queryBlogForeach(Map map);2、编写SQL语句<select id="queryBlogForeach" parameterType="map" resultType="blog"> select * from blog <where> <!-- collection:指定输入对象中的集合属性 item:每次遍历生成的对象 open:开始遍历时的拼接字符串 close:结束时拼接的字符串 separator:遍历对象之间需要拼接的字符串 select * from blog where 1=1 and (id=1 or id=2 or id=3) --> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id=#{id} </foreach> </where> </select>3、测试@Test public void testQueryBlogForeach(){ SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap map = new HashMap(); List<Integer> ids = new ArrayList<Integer>(); ids.add(1); ids.add(2); ids.add(3); map.put("ids",ids); List<Blog> blogs = mapper.queryBlogForeach(map); System.out.println(blogs); session.close(); }小结:其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。多在实践中使用才是熟练掌握它的技巧。12.缓存12.1 简介1、什么是缓存 [ Cache ]?存在内存中的临时数据。将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。2、为什么使用缓存?减少和数据库的交互次数,减少系统开销,提高系统效率。3、什么样的数据能使用缓存?经常查询并且不经常改变的数据。12.2 Mybatis缓存MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)二级缓存需要手动开启和配置,他是基于namespace级别的缓存。为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存12.3 一级缓存一级缓存也叫本地缓存:与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;12.3.1 测试1、在mybatis中加入日志,方便测试结果2、编写接口方法//根据id查询用户 User queryUserById(@Param("id") int id);3、接口对应的Mapper文件<select id="queryUserById" resultType="user"> select * from user where id = #{id} </select>4、测试@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); }5、结果分析12.3.2一级缓存失效的四种情况一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!1、sqlSession不同@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); SqlSession session2 = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); UserMapper mapper2 = session2.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user2 = mapper2.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); session2.close(); }观察结果:发现发送了两条SQL语句!结论:每个sqlSession中的缓存相互独立2、sqlSession相同,查询条件不同@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); UserMapper mapper2 = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user2 = mapper2.queryUserById(2); System.out.println(user2); System.out.println(user==user2); session.close(); }观察结果:发现发送了两条SQL语句!很正常的理解结论:当前缓存中,不存在这个数据3、sqlSession相同,两次查询之间执行了增删改操作!增加方法//修改用户 int updateUser(Map map);编写SQL<update id="updateUser" parameterType="map"> update user set name = #{name} where id = #{id} </update>测试@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); HashMap map = new HashMap(); map.put("name","kuangshen"); map.put("id",4); mapper.updateUser(map); User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); }观察结果:查询在中间执行了增删改操作后,重新执行了结论:因为增删改操作可能会对当前数据产生影响4、sqlSession相同,手动清除一级缓存@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); session.clearCache();//手动清除缓存 User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); }一级缓存就是一个map12.4 二级缓存二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存基于namespace级别的缓存,一个名称空间,对应一个二级缓存;工作机制一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;新的会话查询信息,就可以从二级缓存中获取内容;不同的mapper查出的数据会放在自己对应的缓存(map)中;12.4.1 使用步骤1、开启全局缓存 【mybatis-config.xml】<setting name="cacheEnabled" value="true"/>2、去每个mapper.xml中配置使用二级缓存,这个配置非常简单;【xxxMapper.xml】<cache/> 官方示例=====>查看官方文档 <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> 这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。3、代码测试所有的实体类先实现序列化接口测试代码@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); SqlSession session2 = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); UserMapper mapper2 = session2.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); session.close(); User user2 = mapper2.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session2.close(); }12.4.2 结论只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据查出的数据都会被默认先放在一级缓存中只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中12.5 缓存原理图12.6 第三方缓存-EhCache第三方缓存实现--EhCache: 查看百度百科Ehcache是一种广泛使用的java分布式缓存,用于通用缓存;要在应用程序中使用Ehcache,需要引入依赖的jar包<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.1.0</version> </dependency>在mapper.xml中使用对应的缓存即可<mapper namespace = “org.acme.FooMapper” > <cache type = “org.mybatis.caches.ehcache.EhcacheCache” /> </mapper>编写ehcache.xml文件,如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置。<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <!-- diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下: user.home – 用户主目录 user.dir – 用户当前工作目录 java.io.tmpdir – 默认临时文件路径 --> <diskStore path="./tmpdir/Tmp_EhCache"/> <defaultCache eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU"/> <cache name="cloud_user" eternal="false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LRU"/> <!-- defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。 --> <!-- name:缓存名称。 maxElementsInMemory:缓存最大数目 maxElementsOnDisk:硬盘最大缓存个数。 eternal:对象是否永久有效,一但设置了,timeout将不起作用。 overflowToDisk:是否保存到磁盘,当系统当机时 timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 clearOnFlush:内存数量最大时是否清除。 memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。 FIFO,first in first out,这个是大家最熟的,先进先出。 LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。 LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 --> </ehcache>合理的使用缓存,可以让我们程序的性能大大提升!参考资料1.狂神说Java-Mybatis最新完整教程IDEA版通俗易懂
2022年03月06日
547 阅读
0 评论
0 点赞
2022-03-03
Nginx学习笔记
什么是Nginx?Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。2011年6月1日,nginx 1.0.4发布。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。在全球活跃的网站中有12.18%的使用比率,大约为2220万个网站。Nginx 是一个安装非常的简单、配置文件非常简洁(还能够支持perl语法)、Bug非常少的服务。Nginx 启动特别容易,并且几乎可以做到7*24不间断运行,即使运行数个月也不需要重新启动。你还能够不间断服务的情况下进行软件版本的升级。Nginx代码完全用C语言从头写成。官方数据测试表明能够支持高达 50,000 个并发连接数的响应。Nginx作用?Http代理,反向代理:作为web服务器最常用的功能之一,尤其是反向代理。正向代理反向代理Nginx提供的负载均衡策略有2种:内置策略和扩展策略。内置策略为轮询,加权轮询,Ip hash。扩展策略,就天马行空,只有你想不到的没有他做不到的。轮询加权轮询iphash对客户端请求的ip进行hash操作,然后根据hash结果将同一个客户端ip的请求分发给同一台服务器进行处理,可以解决session不共享的问题。动静分离,在我们的软件开发中,有些请求是需要后台处理的,有些请求是不需要经过后台处理的(如:css、html、jpg、js等等文件),这些不需要经过后台处理的文件称为静态文件。让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作。提高资源响应的速度。目前,通过使用Nginx大大提高了我们网站的响应速度,优化了用户体验,让网站的健壮性更上一层楼!Nginx的安装windows下安装1、下载nginxhttp://nginx.org/en/download.html 下载稳定版本。以nginx/Windows-1.16.1为例,直接下载 nginx-1.16.1.zip。下载后解压,解压后如下:2、启动nginx有很多种方法启动nginx(1)直接双击nginx.exe,双击后一个黑色的弹窗一闪而过(2)打开cmd命令窗口,切换到nginx解压目录下,输入命令 nginx.exe ,回车即可3、检查nginx是否启动成功直接在浏览器地址栏输入网址 http://localhost:80 回车,出现以下页面说明启动成功!4、配置监听nginx的配置文件是conf目录下的nginx.conf,默认配置的nginx监听的端口为80,如果80端口被占用可以修改为未被占用的端口即可。当我们修改了nginx的配置文件nginx.conf 时,不需要关闭nginx后重新启动nginx,只需要执行命令 nginx -s reload 即可让改动生效5、关闭nginx如果使用cmd命令窗口启动nginx, 关闭cmd窗口是不能结束nginx进程的,可使用两种方法关闭nginx(1)输入nginx命令 nginx -s stop(快速停止nginx) 或 nginx -s quit(完整有序的停止nginx)(2)使用taskkill taskkill /f /t /im nginx.exetaskkill是用来终止进程的,/f是强制终止 ./t终止指定的进程和任何由此启动的子进程。/im示指定的进程名称 .linux下安装1、安装gcc安装 nginx 需要先将官网下载的源码进行编译,编译依赖 gcc 环境,如果没有 gcc 环境,则需要安装:yum install gcc-c++2、PCRE pcre-devel 安装PCRE(Perl Compatible Regular Expressions) 是一个Perl库,包括 perl 兼容的正则表达式库。nginx 的 http 模块使用 pcre 来解析正则表达式,所以需要在 linux 上安装 pcre 库,pcre-devel 是使用 pcre 开发的一个二次开发库。nginx也需要此库。命令:yum install -y pcre pcre-devel3、zlib 安装zlib 库提供了很多种压缩和解压缩的方式, nginx 使用 zlib 对 http 包的内容进行 gzip ,所以需要在 Centos 上安装 zlib 库。yum install -y zlib zlib-devel4、OpenSSL 安装OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及 SSL 协议,并提供丰富的应用程序供测试或其它目的使用。nginx 不仅支持 http 协议,还支持 https(即在ssl协议上传输http),所以需要在 Centos 安装 OpenSSL 库。yum install -y openssl openssl-devel5、下载安装包手动下载.tar.gz安装包,地址:https://nginx.org/en/download.html下载完毕上传到服务器上 /root6、解压tar -zxvf nginx-1.18.0.tar.gzcd nginx-1.18.07、配置使用默认配置,在nginx根目录下执行./configure make make install查找安装路径: whereis nginxNginx常用命令cd /usr/local/nginx/sbin/ ./nginx 启动 ./nginx -s stop 停止 ./nginx -s quit 安全退出 ./nginx -s reload 重新加载配置文件 ps aux|grep nginx 查看nginx进程启动成功访问 服务器ip:80注意:如何连接不上,检查阿里云安全组是否开放端口,或者服务器防火墙是否开放端口!相关命令:# 开启 service firewalld start # 重启 service firewalld restart # 关闭 service firewalld stop # 查看防火墙规则 firewall-cmd --list-all # 查询端口是否开放 firewall-cmd --query-port=8080/tcp # 开放80端口 firewall-cmd --permanent --add-port=80/tcp # 移除端口 firewall-cmd --permanent --remove-port=8080/tcp #重启防火墙(修改配置后要重启防火墙) firewall-cmd --reload # 参数解释 1、firwall-cmd:是Linux提供的操作firewall的一个工具; 2、--permanent:表示设置为持久; 3、--add-port:标识添加的端口;参考资料狂神说-Nginx最新教程通俗易懂,40分钟搞定
2022年03月03日
838 阅读
1 评论
0 点赞
2022-03-03
Maven基础学习笔记
Maven基础学习笔记1. Maven简介1.1 Maven是什么Maven的本质是一个项目管理工具,将项目开发过程抽象成一个项目的对象模型(POM)POM(Project Object Model):项目对象模型1.2 Maven的作用项目构建:提供标准的、跨平台的自动化项目构建方式依赖管理:方便快捷的管理项目依赖的资源(jar包),避免资源间版本冲突问题统一开发结构:提供标准的、统一的项目结构2.下载与安装2.1 Maven下载官网:https://maven.apache.org/下载地址:https://maven.apache.org/download.cgi2.2 Maven安装Maven 属于绿色版软件,解压即安装2.3 Maven环境变量配置依赖java,需要配置JAVA_HOME设置Maven自身的环境变量,需要配置MAVEN_HOME并在Path下添加%MAVEN_HOME%\bin测试配置结果mvn3. Maven基础概念3.1 仓库仓库:用于存储资源,包含各种jar包仓库分类:本地仓库:自己电脑上存储资源的仓库,连接远程仓库获取资源远程仓库:非本机电脑的仓库,为本地仓库提供资源中央仓库:Maven团队维护,存储所有资源的仓库私服: 部门/公司范围内存储资源的仓库,从中央仓库获取资源私服的作用:保存具有版权的资源,包含购买或自主研发的jar包中央仓库的jar都是开源的,不能存储具有版权的资源一定范围内共享资源,仅对内部开放,不对外共享3.2 坐标什么是坐标?maven中的坐标用于描述资源的位置https://repo1.maven.org/maven2/Maven坐标组成groupId:定义当前Maven项目隶属于组织名称(通常是域名反写,例如:org.mybatis)artifactld:定义当前Maven项目名称(通常是模块名称,例如CRM\SMS)version:定义当前项目版本号packing:定义项目的打包方式示例:来自https://mvnrepository.com/<!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency>Maven坐标的作用使用唯一标识,唯一性定位资源位置,通过该标识可以将资源的识别与下载工作交给机器(maven工具)来完成4.仓库配置4.1 本地仓库配置修改本地仓库的位置默认位置:${user.home}/.m2/repository自定义位置1.新建好本地文件夹2.修改%MAVEN_HOME%/conf/bin/settings.xml<!-- localRepository | The path to the local repository maven will use to store artifacts. | | Default: ${user.home}/.m2/repository <localRepository>/path/to/local/repo</localRepository> --> # 在下方添加如下内容 <localRepository>/path/to/local/repo</localRepository>4.2 远程仓库配置镜像在setting文件中配置阿里云镜像仓库修改%MAVEN_HOME%/conf/bin/settings.xml<mirrors> # 在这里面添加 <mirror> <id>alimaven</id> <name>aliyun maven</name> <url>https://maven.aliyun.com/repository/public</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors>5. 第一个Maven项目(手工制作)5.1 Maven工程目录结构在src同层目录下创建pom.xml文件<!-- 属性 --> <properties> <spring.version>4.2.4.RELEASE</spring.version> <hibernate.version>5.0.7.Final</hibernate.version> <struts.version>2.3.24</struts.version> </properties> <!-- 锁定版本,struts2-2.3.24、spring4.2.4、hibernate5.0.7 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>${struts.version}</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-spring-plugin</artifactId> <version>${struts.version}</version> </dependency> </dependencies> </dependencyManagement> <!-- 依赖管理 --> <dependencies> <!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <!-- hibernate --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> </dependency> <!-- 数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> <scope>runtime</scope> </dependency> <!-- c3p0 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!-- 导入 struts2 --> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-spring-plugin</artifactId> </dependency> <!-- servlet jsp --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <!-- 日志 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.2</version> </dependency> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> <scope>test</scope> </dependency> <!-- jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies> <build> <plugins> <!-- 设置编译版本为1.7 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- maven内置 的tomcat6插件 --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <version>1.1</version> <configuration> <!-- 可以灵活配置工程路径 --> <path>/Hello</path> <!-- 可以灵活配置端口号 --> <port>8080</port> </configuration> </plugin> </plugins> </build>5.2 Maven项目构建命令Maven构建命令使用mvn开头,后面添加功能参数,可以一次执行多个命令,使用空格分隔mvn compile #编译 mvn clean #清理 mvn test #测试 mvn package #打包 mvn install #安装到本地仓库手动执行的话在pom.xml文件目录下执行6.第一个Maven项目(IDEA生成)https://www.bilibili.com/video/BV1Ah411S7ZE?p=9&spm_id_from=pageDriverjava项目web项目7.依赖管理7.1 添加依赖在pom.xml文件里的<dependencies></dependencies>中添加<dependency></dependency>7.2 依赖传递依赖具有传递性直接依赖:在当前项目中通过依赖配置建立的依赖关系间接依赖:被资源的资源如果依赖其他资源,当前项目间接依赖其他资源7.3 依赖传递冲突问题路径优先:当依赖中出现相同资源时,层级越深,优先级越低,层级越浅,优先级越高声明优先:当资源在相同层级被依赖时,配置顺序靠前的覆盖顺序靠后的特殊优先:当同级配置了相同资源的不同版本,后配置的覆盖先配置的(在同一个pom.xml文件中进行配置)7.4 可选依赖与排除依赖可选依赖控制资源不被调用者看到具体的依赖情况排除依赖指主动断开依赖的资源,被排除的资源无需要指定具体的版本--不需要7.5 依赖范围依赖的jar默认情况可以在任何地方使用,可以通过scope标签设定其作用范围作用范围主程序范围内有效(main文件夹范围内)测试程序范围有效(test文件夹范围内)是否参与打包(package指令范围内)依赖范围传递性(了解即可)带有依赖范围的资源在进行传递时,作用范围将受到影响8.生命周期与插件8.1 项目构建生命周期maven构建生命周期描述的是一次构建过程经历了多少个事件maven对项目构建的生命周期划分为3套clean:清理工作default:核心工作,例如编译、测试、打包、部署等site:产生报告,发布站点等8.2 插件插件与生命周期内的阶段绑定,在执行到对应生命周期时执行对应的插件功能默认maven在各个生命周期上绑定有预设的功能通过插件可以自定义其他功能参考资料黑马程序员Maven全套教程,maven项目管理从基础到高级
2022年03月03日
810 阅读
0 评论
0 点赞
2022-01-16
TensorBoard:训练日志及网络结构可视化工具
1.安装pip install tensorboard2.可视化标量数据(loss和accuracy)from torch.utils.tensorboard import SummaryWriter import random # 实例化TensorBoard logs_writer = SummaryWriter('./logs') # 可视化标量数据 for epoch_id in range(100): logs_writer.add_scalar("train/loss",random.random(),epoch_id) logs_writer.add_scalar("train/accuracy",random.random(),epoch_id) logs_writer.add_scalar("test/loss",random.random(),epoch_id) logs_writer.add_scalar("test/accuracy",random.random(),epoch_id)tensorboard --logdir=logs --bind_all3.可视化网络结构""" 可视化网络结构 """ import torch import torch.nn as nn from torch.utils.tensorboard import SummaryWriter # 实例化TensorBoard writer = SummaryWriter('./model_vis') # 构建网络模型 - 使用自定义类 class Digit_Rec(nn.Module): def __init__(self): super(Digit_Rec,self).__init__() self.conv1 = nn.Conv2d(1,10,5) #1:灰度图片的通道,10:输出通道,5:kernel self.relu1 = nn.ReLU() self.max_pool = nn.MaxPool2d(2,2) self.conv2 = nn.Conv2d(10,20,3) #10:输入通道,20:输出通道,3:Kernel self.relu2 = nn.ReLU() self.fc1 = nn.Linear(20*10*10,500) # 20*10*10:输入通道,500:输出通道 self.relu3 = nn.ReLU() self.fc2 = nn.Linear(500,10) # 500:输入通道,10:输出通道 self.relu4 = nn.ReLU() self.softmax = nn.Softmax(dim=1) def forward(self,x): batch_size = x.size(0) # x的格式:batch_size x 1 x 28 x 28 拿到了batch_size x = self.conv1(x) # 输入:batch*1*28*28 输出:batch*10*24*24 x = self.relu1(x) x = self.max_pool(x) # 输入:batch*10*24*24输出:batch*10*12*12 x = self.conv2(x) x = self.relu2(x) x = x.view(batch_size,-1) #fatten 展平 -1自动计算维度,20*10*10=2000 x = self.fc1(x) # 输入:batch*2000 输出:batch*500 x = self.relu3(x) x = self.fc2(x) # 输入:batch*500 输出:batch*10 x = self.relu4(x) output = self.softmax(x) # 计算分类后,每个数字的概率值 return output model = Digit_Rec() model = Digit_Rec() images = torch.randn(1, 1, 28, 28) writer.add_graph(model, images) writer.close()tensorboard --logdir=model_vis --bind_all参考资料Pytorch中使用tensorboard学习笔记(2)记录损失loss和准确率accuracypytorch中使用tensorboard绘制Accuracy/Loss曲线(train和test显示在同一幅图中)pytorch中使用TensorBoard进行可视化Loss及特征图
2022年01月16日
12,687 阅读
11 评论
0 点赞
2022-01-15
MyGraphPaper:免费在线方格笔记本制作工具
官方网站:https://www.mygraphpaper.com/index.php?lang=zh-hans如果你的笔记本纸用完了,买,是一种解决方案。自己做,也可以。而且更快更省钱。你还能想做什么样式,就做什么样式,用 MyGraphPaper 这样的在线工具就可以。在方格纸生成界面上,有包括笔记纸、方格纸、网格纸、画稿纸、乐谱纸等 17 种类型的笔记纸可供选择。在上方选择好类型后,下面还能对笔记纸进行自定义调整。在纸张设定里,提供了纸张类别、自定义大小、方向等多项自定义选项,你还可以自行选择插入小标题。下方还提供了更为细致的自定义选项。你可以在这里自定义间隔线、网点、线条、文字等多种格式。不同的纸张类型提供了不同的自定义选项。想要制作一张康奈尔笔记纸?点击模板,即可得到一张这样的页面。如果我们想要得到只有横向间隔线的页面,将直间隔线取消勾选即可;如果我们想要获得纯白页面,只需要将间隔线取消勾选,同时取消文字和水印。想镜像整个页面,调整方块位置即可。版式设计完成后,点击 PDF 输出下载,即可预览笔记纸。官方为我们提供了 PDF 格式的下载以及快速打印功能。一张自己独有的笔记纸,就这样轻松做出来了。参考资料https://topbook.cc/search/article?keywords=%E4%BD%9C%E4%B8%9A%E7%BA%B8&selectedArticle=1820
2022年01月15日
824 阅读
0 评论
0 点赞
2022-01-15
MySQL学习:SQL查询语句中select、 from、 where、 group by 、having、 order by的执行顺序
1. 字段解释from:需要从哪个数据表检索数据where:过滤表中数据的条件group by:如何将上面过滤出的数据分组having:对上面已经分组的数据进行过滤的条件select:查看结果集中的哪个列,或列的计算结果order by:按照什么样的顺序来查看返回的数据2.书写顺序与执行顺序书写顺序依次为:select>from>where>group by>having>order by # 其中select和from是必须的。执行顺序依次为:from>where>group by>having>select>order byfrom后面的表关联,是自右向左解析的;而where条件的解析顺序是从左往右的。也就是说,在写SQL语句的时候,尽量把数据量小的表放在最右边来进行关联(用小表去匹配大表),而把能筛选出小量数据的条件放在where语句的最左边 (用小表去匹配大表)。参考资料sql语句中select、 from、 where、 group by 、having、 order by的执行顺序分析SQL查询语句中select from where group by having order by的执行顺序
2022年01月15日
910 阅读
0 评论
0 点赞
2022-01-15
java学习:字符串的格式化及日期时间格式化
1. 常规类型格式化为字符串1.1 转换符转 换 符说 明示 例%s字符串类型"mingrisoft"%c字符类型'm'%b布尔类型true%d整数类型(十进制)99%x整数类型(十六进制)FF%o整数类型(八进制)77%f浮点类型99.99%a十六进制浮点类型FF.35AE%e指数类型9.38e+5%g通用浮点类型(f和e类型中较短的) %h散列码 %%百分比类型%%n换行符 %tx日期与时间类型(x代表不同的日期与时间转换符 1.2.使用案例public static void main(String[] args) { String str=null; str=String.format("Hi,%s", "王力"); System.out.println(str); str=String.format("Hi,%s:%s.%s", "王南","王力","王张"); System.out.println(str); System.out.printf("字母a的大写是:%c %n", 'A'); System.out.printf("3>7的结果是:%b %n", 3>7); System.out.printf("100的一半是:%d %n", 100/2); System.out.printf("100的16进制数是:%x %n", 100); System.out.printf("100的8进制数是:%o %n", 100); System.out.printf("50元的书打8.5折扣是:%f 元%n", 50*0.85); System.out.printf("上面价格的16进制数是:%a %n", 50*0.85); System.out.printf("上面价格的指数表示:%e %n", 50*0.85); System.out.printf("上面价格的指数和浮点数结果的长度较短的是:%g %n", 50*0.85); System.out.printf("上面的折扣是%d%% %n", 85); System.out.printf("字母A的散列码是:%h %n", 'A'); }输出结果Hi,王力 Hi,王南:王力.王张 字母a的大写是:A 3>7的结果是:false 100的一半是:50 100的16进制数是:64 100的8进制数是:144 50元的书打8.5折扣是:42.500000 元 上面价格的16进制数是:0x1.54p5 上面价格的指数表示:4.250000e+01 上面价格的指数和浮点数结果的长度较短的是:42.5000 上面的折扣是85% 字母A的散列码是:411.3 搭配的标志标 志说 明示 例结 果+为正数或者负数添加符号("%+d",15)+15−左对齐("%-5d",15)\15 \ 0数字前面补0("%04d", 99)0099空格在整数之前添加指定数量的空格("% 4d", 99)\99\ ,以“,”对数字分组("%,f", 9999.99)9,999.990000(使用括号包含负数("%(f", -99.99)(99.990000)#如果是浮点数则包含小数点,如果是16进制或8进制则添加0x或0("%#x", 99)("%#o", 99)0x63 0143<格式化前一个转换符所描述的参数("%f和%<3.2f", 99.45)99.450000和99.45$被格式化的参数索引("%1$d,%2$s", 99,"abc")99,abc1.4.使用案例public static void main(String[] args) { String str=null; //$使用 str=String.format("格式参数$的使用:%1$d,%2$s", 99,"abc"); System.out.println(str); //+使用 System.out.printf("显示正负数的符号:%+d与%d%n", 99,-99); //补O使用 System.out.printf("最牛的编号是:%03d%n", 7); //空格使用 System.out.printf("Tab键的效果是:% 8d%n", 7); //,使用 System.out.printf("整数分组的效果是:%,d%n", 9989997); //空格和小数点后面个数 System.out.printf("一本书的价格是:% 50.5f元%n", 49.8); }输出结果格式参数$的使用:99,abc 显示正负数的符号:+99与-99 最牛的编号是:007 Tab键的效果是: 7 整数分组的效果是:9,989,997 一本书的价格是: 49.80000元 2. 日期格式化2.1 转换符转 换 符说 明示 例%tx日期与时间类型(x代表不同的日期与时间转换符 c包括全部日期和时间信息星期六 十月 27 14:21:20 CST 2007F“年-月-日”格式2007-10-27D“月/日/年”格式10/27/07r“HH:MM:SS PM”格式(12时制)02:25:51 下午T“HH:MM:SS”格式(24时制)14:28:16R“HH:MM”格式(24时制)14:282.2 使用案例public static void main(String[] args) { Date date=new Date(); //c的使用 System.out.printf("全部日期和时间信息:%tc%n",date); //f的使用 System.out.printf("年-月-日格式:%tF%n",date); //d的使用 System.out.printf("月/日/年格式:%tD%n",date); //r的使用 System.out.printf("HH:MM:SS PM格式(12时制):%tr%n",date); //t的使用 System.out.printf("HH:MM:SS格式(24时制):%tT%n",date); //R的使用 System.out.printf("HH:MM格式(24时制):%tR",date); }输出结果全部日期和时间信息:星期一 九月 10 10:43:36 CST 2012 年-月-日格式:2012-09-10 月/日/年格式:09/10/12 HH:MM:SS PM格式(12时制):10:43:36 上午 HH:MM:SS格式(24时制):10:43:36 HH:MM格式(24时制):10:43public static void main(String[] args) { Date date=new Date(); //b的使用,月份简称 String str=String.format(Locale.US,"英文月份简称:%tb",date); System.out.println(str); System.out.printf("本地月份简称:%tb%n",date); //B的使用,月份全称 str=String.format(Locale.US,"英文月份全称:%tB",date); System.out.println(str); System.out.printf("本地月份全称:%tB%n",date); //a的使用,星期简称 str=String.format(Locale.US,"英文星期的简称:%ta",date); System.out.println(str); //A的使用,星期全称 System.out.printf("本地星期的简称:%tA%n",date); //C的使用,年前两位 System.out.printf("年的前两位数字(不足两位前面补0):%tC%n",date); //y的使用,年后两位 System.out.printf("年的后两位数字(不足两位前面补0):%ty%n",date); //j的使用,一年的天数 System.out.printf("一年中的天数(即年的第几天):%tj%n",date); //m的使用,月份 System.out.printf("两位数字的月份(不足两位前面补0):%tm%n",date); //d的使用,日(二位,不够补零) System.out.printf("两位数字的日(不足两位前面补0):%td%n",date); //e的使用,日(一位不补零) System.out.printf("月份的日(前面不补0):%te",date); }输出结果英文月份简称:Sep 本地月份简称:九月 英文月份全称:September 本地月份全称:九月 英文星期的简称:Mon 本地星期的简称:星期一 年的前两位数字(不足两位前面补0):20 年的后两位数字(不足两位前面补0):12 一年中的天数(即年的第几天):254 两位数字的月份(不足两位前面补0):09 两位数字的日(不足两位前面补0):10 月份的日(前面不补0):103.时间格式化3.1 转换符转 换 符说 明示 例H2位数字24时制的小时(不足2位前面补0)15I2位数字12时制的小时(不足2位前面补0)03k2位数字24时制的小时(前面不补0)15l2位数字12时制的小时(前面不补0)3M2位数字的分钟(不足2位前面补0)03S2位数字的秒(不足2位前面补0)09L3位数字的毫秒(不足3位前面补0)015N9位数字的毫秒数(不足9位前面补0)562000000p小写字母的上午或下午标记中:下午英:pmz相对于GMT的RFC822时区的偏移量+0800Z时区缩写字符串CSTs1970-1-1 00:00:00 到现在所经过的秒数1193468128Q1970-1-1 00:00:00 到现在所经过的毫秒数11934681289843.2 使用案例public static void main(String[] args) { Date date = new Date(); //H的使用 System.out.printf("2位数字24时制的小时(不足2位前面补0):%tH%n", date); //I的使用 System.out.printf("2位数字12时制的小时(不足2位前面补0):%tI%n", date); //k的使用 System.out.printf("2位数字24时制的小时(前面不补0):%tk%n", date); //l的使用 System.out.printf("2位数字12时制的小时(前面不补0):%tl%n", date); //M的使用 System.out.printf("2位数字的分钟(不足2位前面补0):%tM%n", date); //S的使用 System.out.printf("2位数字的秒(不足2位前面补0):%tS%n", date); //L的使用 System.out.printf("3位数字的毫秒(不足3位前面补0):%tL%n", date); //N的使用 System.out.printf("9位数字的毫秒数(不足9位前面补0):%tN%n", date); //p的使用 String str = String.format(Locale.US, "小写字母的上午或下午标记(英):%tp", date); System.out.println(str); System.out.printf("小写字母的上午或下午标记(中):%tp%n", date); //z的使用 System.out.printf("相对于GMT的RFC822时区的偏移量:%tz%n", date); //Z的使用 System.out.printf("时区缩写字符串:%tZ%n", date); //s的使用 System.out.printf("1970-1-1 00:00:00 到现在所经过的秒数:%ts%n", date); //Q的使用 System.out.printf("1970-1-1 00:00:00 到现在所经过的毫秒数:%tQ%n", date); }输出结果2位数字24时制的小时(不足2位前面补0):11 2位数字12时制的小时(不足2位前面补0):11 2位数字24时制的小时(前面不补0):11 2位数字12时制的小时(前面不补0):11 2位数字的分钟(不足2位前面补0):03 2位数字的秒(不足2位前面补0):52 3位数字的毫秒(不足3位前面补0):773 9位数字的毫秒数(不足9位前面补0):773000000 小写字母的上午或下午标记(英):am 小写字母的上午或下午标记(中):上午 相对于GMT的RFC822时区的偏移量:+0800 时区缩写字符串:CST 1970-1-1 00:00:00 到现在所经过的秒数:1347246232 1970-1-1 00:00:00 到现在所经过的毫秒数:1347246232773参考资料Java 字符串格式化详解JAVA字符串格式化-String.format()的使用java字符串格式化(String类format方法)
2022年01月15日
547 阅读
0 评论
0 点赞
2022-01-15
nginx学习:nginx的请求转发算法(负载均衡配置)
1.nginx支持的负载均衡调度算法1.1 轮询(默认算法):每个请求按时间顺序分配到不同后端服务器,如果某个后端服务器宕机,能自动剔除掉。基于iphttp { ..... 其他的内容 #定义上游服务器集群,定义一个负载均衡器 upstream myweb1 { server 192.168.0.161; server 192.168.0.162; server 192.168.0.163; } server { listen 80; location / { proxy_pass http://myweb1; } } }基于域名http { ..... 其他的内容 #定义上游服务器集群,定义一个负载均衡器 upstream myweb1 { server 192.168.0.161; server 192.168.0.162; server 192.168.0.163; } server { listen 80; server_name www.sc.com; location / { proxy_pass http://myweb1; } } }1.2 weight轮询nginx反向代理接受到客户端收到的请求后,可以给不同的后端服务器设置一个权重值(weight),用于调整不同服务器上请求的分配率,权重数据越大,被分配到请求的几率越大;该权重值,主要是针对实际工作环境中不同的后端服务器配置进行配置的。比如说有些服务器的硬件配置高,比重就会比较大一点。http { ..... 其他的内容 #定义上游服务器集群,定义一个负载均衡器 upstream myweb1 { server 192.168.0.161 weight=1; server 192.168.0.162 weight=3; server 192.168.0.163 weight=6; } server { listen 80; server_name www.sc.com; location / { proxy_pass http://myweb1; } } }1.3 ip_hash每个请求按照发起客户端ip的hash结果进行匹配,这样的算法每一个固定的ip地址的客户端总会访问到同一个后端服务器,这也在一定程度上解决了集群部署环境下session共享的问题。http { ..... 其他的内容 #定义上游服务器集群,定义一个负载均衡器 upstream myweb1 { ip_hash; server 192.168.0.161; server 192.168.0.162; server 192.168.0.163; } server { listen 80; server_name www.sc.com; location / { proxy_pass http://myweb1; } } }1.4 least-connected–最小连接数将下一个请求分配给活动连接数量最少的服务器http { ..... 其他的内容 #定义上游服务器集群,定义一个负载均衡器 upstream myweb1 { least_conn; server 192.168.0.161; server 192.168.0.162; server 192.168.0.163; } server { listen 80; server_name www.sc.com; location / { proxy_pass http://myweb1; } } }1.5 fair:智能调整调度算法(第三方)动态的根据后端服务器的请求处理器的请求处理响应的时间来进行均衡分配,响应时间短,处理效率高的服务器分配到请求的概率高,响应时间长,处理效率低的服务器分配到的请求少;结合了前两者的优点的一种调度算法。但是需要注意的是nginx默认不支持fair算法,如果要使用这种算法,需要安装upstream_fair模块。1.6 url_hash(第三方)按照访问的url的hash结果分配请求,每个请求的url会指向后端固定的某个服务器,可以在nginx作为静态服务器的情况下提高缓存效率。同样要注意Nginx默认不支持这种调度算法,要使用的话需要安装nginx的hash软件包。http { ..... 其他的内容 #定义上游服务器集群,定义一个负载均衡器 upstream myweb1 { server 192.168.0.161; server 192.168.0.162; server 192.168.0.163; hash $request_uri; } server { listen 80; server_name www.sc.com; location / { proxy_pass http://myweb1; } } }在upstream模块中,可以通过server命令指定后端服务器的IP地址和端口,同时还可以设置每台后端服务器在负载均衡调度中的状态,常用的状态有以下几种:1、down:表示当前server暂时不参与负载均衡。2、backup:预留的备份机,当其他所有非backup机器出现故障或者繁忙的时候,才会请求backup机器,这台机器的访问压力最轻。3、max_fails:允许请求的失败次数,默认为1,配合fail_timeout一起使用4、fail_timeout:经历max_fails次失败后,暂停服务的时间,默认为10s(某个server连接失败了max_fails次,则nginx会认为该server不工作了。同时,在接下来的 fail_timeout时间内,nginx不再将请求分发给失效的server。)参考资料你知道nginx的请求转发算法,如何配置根据权重转发3. nginx的请求转发算法,如何配置根据权重转发Nginx负载均衡调度算法及配置案例Nginx几种负载均衡算法及配置实例Nginx配置之实现多台服务器负载均衡
2022年01月15日
648 阅读
0 评论
0 点赞
2022-01-15
java学习:Junit4单元测试的基本用法及注解和执行顺序
1. Junit最简单的用法案例新建一个类被测试类,里面包含一些测试方法package junit.util; /** * 被测试类,通过Junit对此类的方法进行单元测试 */ public class Claculate { public int add(int a, int b) { return a + b; } public int subtract(int a, int b) { return a - b; } public int multiply(int a, int b) { return a * b; } public int divide(int a, int b) { return a / b; } }新建一个Junit的测试类用来测试上面的测试方法,新增Junit的测试类方法如下:package junit.util; import static org.junit.Assert.*; import junit.util.Claculate; import org.junit.Test; /** * junit的测试方法必须使用@Test注解 * 测试方法必须以public void修饰,并且不包含参数 */ public class ClaculateTest { @Test public void testAdd() { /** * assertEquals这个方法是一个断言方法 * 第一个参数表示预期的结果 * 第二个参数表示程序的执行结果 * 当预期结果与执行结果是一致的时候,则表示单元测试成功 */ assertEquals(4, new Claculate().add(1, 3)); } @Test public void testSubtract() { assertEquals(4, new Claculate().subtract(9, 5)); } @Test public void testMultiply() { assertEquals(6, new Claculate().multiply(2, 3)); } @Test(expected=ArithmeticException.class) public void testDivide() { assertEquals(3, new Claculate().divide(9, 0)); } }上面的这个测试类,分别对被测试类Claculate的四个方法进行了测试,测试是选择使用Junit方式进行执行,如果想要执行单个测试方法,可以选择单个方法进行执行2.junit中的常用注解及执行顺序2.1 常用注解为什么引入注解?在实际项目中,进行JUnit测试时,通常会涉及到一些初始化的东西,可能有些配置项需要在测试前进行加载的,JUnit提供了一些初始化的方法用于初始化注解名作用@Before初始化方法,对于每一个测试方法都要执行一次(注意与BeforeClass区别,后者是对于所有方法执行一次)@After释放资源,对于每一个测试方法都要执行一次(注意与AfterClass区别,后者是对于所有方法执行一次)@Test测试方法,在这里可以测试期望异常和超时时间@Ignore忽略的测试方法@BeforeClass针对所有测试,只执行一次,且必须为static void@AfterClass针对所有测试,只执行一次,且必须为static void2.2 执行顺序一个JUnit4的单元测试用例执行顺序为:@BeforeClass -> @Before -> @Test -> @After -> @AfterClass; 每一个测试方法的调用顺序为:@Before -> @Test -> @After; 参考资料junit用法,before,beforeClass,after, afterClass的执行顺序Junit4单元测试的基本用法 Junit的基本使用(详解)
2022年01月15日
791 阅读
0 评论
0 点赞
2022-01-14
[转载]:Java 相关一些面试题
junit 用法,before,beforeClass,after, afterClass 的执行顺序分布式锁nginx 的请求转发算法,如何配置根据权重转发用 hashmap 实现 redis 有什么问题(死锁,死循环,可用 ConcurrentHashmap)线程的状态线程的阻塞的方式sleep 和 wait 的区别hashmap 的底层实现一万个人抢 100 个红包,如何实现(不用队列),如何保证 2 个人不能抢到同一个红包,可用分布式锁java 内存模型,垃圾回收机制,不可达算法两个 Integer 的引用对象传给一个 swap 方法在方法内部交换引用,返回后,两个引用的值是否会发现变化aop 的底层实现,动态代理是如何动态,假如有 100 个对象,如何动态的为这 100 个对象代理是否用过 maven install。 maven test。git(make install 是安装本地 jar 包)tomcat 的各种配置,如何配置 docBasespring 的 bean 配置的几种方式web.xml 的配置spring 的监听器。zookeeper 的实现机制,有缓存,如何存储注册服务的IO 会阻塞吗?readLine 是不是阻塞的用过 spring 的线程池还是 java 的线程池?字符串的格式化方法 (20,21 这两个问题问的太低级了)时间的格式化方法定时器用什么做的线程如何退出结束java 有哪些锁?乐观锁 悲观锁 synchronized 可重入锁 读写锁,用过 reentrantlock 吗?reentrantlock 与 synmchronized 的区别ThreadLocal 的使用场景java 的内存模型,垃圾回收机制为什么线程执行要调用 start 而不是直接 run(直接 run,跟普通方法没什么区别,先调 start,run 才会作为一个线程方法运行)qmq 消息的实现机制 (qmq 是去哪儿网自己封装的消息队列)遍历 hashmap 的三种方式jvm 的一些命令memcache 和 redis 的区别mysql 的行级锁加在哪个位置ConcurrentHashmap 的锁是如何加的?是不是分段越多越好myisam 和 innodb 的区别(innodb 是行级锁,myisam 是表级锁)mysql 其他的性能优化方式linux 系统日志在哪里看如何查看网络进程统计一个整数的二进制表示中 bit 为 1 的个数jvm 内存模型,java 内存模型如何把 java 内存的数据全部 dump 出来如何手动触发全量回收垃圾,如何立即触发垃圾回收hashmap 如果只有一个写其他全读会出什么问题git rebasemongodb 和 hbase 的区别如何解决并发问题volatile 的用途java 线程池(好像之前我的理解有问题)mysql 的 binlog代理模式mysql 是如何实现事务的读写分离何时强制要读主库,读哪个从库是通过什么方式决定的,从库的同步 mysql 用的什么方式mysql 的存储引擎mysql 的默认隔离级别,其他隔离级别将一个链表反转(用三个指针,但是每次只发转一个)spring Aop 的实现原理,具体说说何时会内存泄漏,内存泄漏会抛哪些异常是否用过 Autowire 注解spring 的注入 bean 的方式sql 语句各种条件的执行顺序,如 select, where, order by, group byselect xx from xx where xx and xx order by xx limit xx; 如何优化这个(看 explain)四则元算写代码统计 100G 的 ip 文件中出现 ip 次数最多的 100 个 ipzookeeper 的事物,结点,服务提供方挂了如何告知消费方5 台服务器如何选出 leader (选举算法)适配器和代理模式的区别读写锁static 加锁事务隔离级别门面模式,类图 (外观模式)mybatis 如何映射表结构二叉树遍历主从复制mysql 引擎区别静态内部类加载到了哪个区?方法区class 文件编译后加载到了哪web 的 http 请求如何整体响应时间变长导致处理的请求数变少,该如何处理?用队列,当处理不了那么多 http 请求时将请求放到队列中慢慢处理,web 如何实现队列线程安全的单例模式快速排序性能考虑volatile 关键字用法求表的 size,或做数据统计可用什么存储引擎读多写少可用什么引擎假如要统计多个表应该用什么引擎concurrenhashmap 求 size 是如何加锁的,如果刚求完一段后这段发生了变化该如何处理1000 个苹果放 10 个篮子,怎么放,能让我拿到所有可能的个数可重入的读写锁,可重入是如何实现的?是否用过 NIOjava 的 concurrent 包用过没sting s=new string (“abc”) 分别在堆栈上新建了哪些对象java 虚拟机的区域分配,各区分别存什么分布式事务(JTA)threadlocal 使用时注意的问题(ThreadLocal 和 Synchonized 都用于解决多线程并发访问。但是 ThreadLocal 与 synchronized 有本质的区别。synchronized 是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而 ThreadLocal 为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而 Synchronized 却正好相反,它用于在多个线程间通信时能够获得数据共享)java 有哪些容器 (集合,tomcat 也是一种容器)二分查找算法myisam 的优点,和 innodb 的区别redis 能存哪些类型http 协议格式,get 和 post 的区别可重入锁中对应的 wait 和 notifyredis 能把内存空间交换进磁盘中吗 (这个应该是可以的,但是那个面试官非跟我说不可以)java 线程池中基于缓存和基于定长的两种线程池,当请求太多时分别是如何处理的?定长的事用的队列,如果队列也满了呢?交换进磁盘?基于缓存的线程池解决方法呢?synchronized 加在方法上用的什么锁可重入锁中的 lock 和 trylock 的区别innodb 对一行数据的读会枷锁吗?不枷锁,读实际读的是副本redis 做缓存是分布式存的?不同的服务器上存的数据是否重复?guavacache 呢?是否重复?不同的机器存的数据不同用 awk 统计一个 ip 文件中 top10对表做统计时可直接看 schema info 信息,即查看表的系统信息mysql 目前用的版本公司经验丰富的人给了什么帮助?(一般 boss 面会问这些)自己相对于一样的应届生有什么优势自己的好的总结习惯给自己今后的工作带了什么帮助,举例为证原子类,线程安全的对象,异常的处理方式4 亿个 int 数,如何找出重复的数(用 hash 方法,建一个 2 的 32 次方个 bit 的 hash 数组,每取一个 int 数,可 hash 下 2 的 32 次方找到它在 hash 数组中的位置,然后将 bit 置 1 表示已存在)4 亿个 url,找出其中重复的(考虑内存不够,通过 hash 算法,将 url 分配到 1000 个文件中,不同的文件间肯定就不会重复了,再分别找出重复的)有 1 万个数组,每个数组有 1000 个整数,每个数组都是降序的,从中找出最大的 N 个数,N<1000LinkedHashmap 的底层实现类序列化时类的版本号的用途,如果没有指定一个版本号,系统是怎么处理的?如果加了字段会怎么样?Override 和 Overload 的区别,分别用在什么场景java 的反射是如何实现的参考资料Java 相关一些面试题
2022年01月14日
475 阅读
0 评论
0 点赞
2022-01-13
MySQL学习:常用函数总结
1.数学函数针对数字-- ABS(x) 返回x的绝对值 SELECT ABS(-1); -- 返回1 -- ROUND(x)返回离 x 最近的整数 SELECT ROUND(1.23456); -- 返回1 -- CEIL(x)/CEILING(x) 返回大于或等于 x 的最小整数 SELECT CEIL(1.5); -- 返回2 SELECT CEILING(1.5); -- 返回2 -- FLOOR(x) 返回小于或等于 x 的最大整数 SELECT FLOOR(1.5); -- 返回1 -- POW(x,y)/POWER(x,y)返回 x 的 y 次方 SELECT POW(2,3); -- 返回8 SELECT POWER(2,3); -- 返回8 -- RAND()返回 0 到 1 的随机数 SELECT RAND(); -- SIGN(x)返回 x 的符号,x 是负数、0、正数分别返回 -1、0 和 1 SELECT SIGN(-10); -- 返回-1 -- SQRT(x)返回x的平方根 SELECT SQRT(25); -- 返回5 -- TRUNCATE(x,y)返回数值 x 保留到小数点后 y 位的值,不会进行四舍五入 SELECT TRUNCATE(1.23456,3); -- 返回1.234针对字段-- AVG(expression) 返回一个表达式的平均值,expression 是一个字段 SELECT AVG(age) FROM student; -- MAX(expression)返回字段 expression 中的最大值 SELECT MAX(age) AS maxAge FROM Student; -- MIN(expression)返回字段 expression 中的最大值 SELECT MIN(age) AS minAge FROM Student; -- SUM(expression)返回指定字段的总和 SUM(expression)返回指定字段的总和2.字符串函数-- LENGTH/CHAR_LENGTH(s)/CHARACTER_LENGTH(s)返回字符串 s 的字符数 SELECT LENGTH('1234'); -- 返回4 -- CONCAT(s1,s2…sn)字符串 s1,s2 等多个字符串合并为一个字符串 SELECT CONCAT('hel','llo'); -- 返回hello -- LOCATE(s1,s)从字符串 s 中获取 s1 的开始位置 SELECT LOCATE('st','myteststring'); -- 返回5 -- LCASE(s)/LOWER(s)将字符串 s 的所有字母变成小写字母 SELECT LOWER('RUNOOB'); -- 返回runoob -- UCASE(s)/UPPER(s)将字符串 s 的所有字母变成大写字母 SELECT UCASE('runoob'); -- 返回RUNOOB -- TRIM(s)去掉字符串 s 开始和结尾处的空格 SELECT TRIM(' RUNOOB ');-- 返回RUNOOB -- SUBSTR/SUBSTRING(s, start, length)从字符串 s 的 start 位置截取长度为 length 的子字符串 SELECT SUBSTR/SUBSTRING("RUNOOB", 2, 3);-- 从字符串 RUNOOB 中的第 2 个位置截取 3个 字符,返回UNO -- REVERSE(s)将字符串s的顺序反过来 SELECT REVERSE('abc');-- 返回cba -- REPLACE()替换出现的指定字符串 SELECT REPLACE('狂神说坚持就能成功','坚持','努力'); -- 替换出现的指定字符串 SELECT REPIACE(studentname,'周','邹') FROM student WHERE studentname LIKE '%周';3.时间日期函数-- CURDATE()/CURRENT_DATE()返回当前日期 SELECT CURDATE();-- 返回2019-02-19 SELECT CURRENT_DATE(); -- 返回2019-02-19 -- CURRENT_TIME()/CURTIME()返回当前时间 SELECT CURRENT_TIME(); -- 返回11:40:45 -- NOW()返回当前日期和时间 SELECT NOW();-- 返回2019-02-19 11:41:32 SELECT LOCALTIME(); -- 本地时间 SELECT SYSDATE(); -- 系统时间 -- 提取年月日时分秒 SELECT YEAR(NOW()); SELECT MONTH(NOW()); SELECT DAY(NOW()); SELECT HOUR(NOW()); SELECT MINUTE(NOW()); SELECT SECOND(NOW());4.系统函数-- CURRENT_USER()/SESSION_USER()/SYSTEM_USER()/USER()返回当前用户 SELECT USER(); -- DATABASE()返回当前数据库名 SELECT DATABASE(); -- VERSION()返回数据库的版本号 SELECT VERSION();参考资料MySQL 5.7 参考手册12.6 数值函数和运算符12.8 字符串函数和运算符12.7 日期和时间函数MySQL常用函数大全(总结篇)
2022年01月13日
623 阅读
0 评论
0 点赞
2022-01-13
Linux的SWAP交换分区设置准则
1.SWAP分区的作用当Linux系统的物理内存不够用的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间被临时保存到Swap空间中,等到那些程序要运行时,再从Swap中恢复保存的数据到内存中。2.SWAP分区设置准则Redhat官方的文档中关于swap分区大小设置的建议物理内存建议的交换空间大小如果开启休眠功能建议的交换空间大小⩽ 2GB内存的2倍内存的3倍> 2GB – 8GB等于内存大小内存的2倍> 8GB – 64GB至少4G内存的1.5倍> 64GB至少4G不建议使用休眠因此,一般来说可以按照如下规则设置swap大小:物理内存交换空大小⩽ 2GB内存的2倍> 2GB – 4GB4GB> 4GB – 8GB等于内存大小> 8GB – 64GB8GB> 64GB16GB3.swappiness参数3.1 系统在什么情况下才会使用SWAP实际上,并不是等所有的物理内存都消耗完毕之后,才去使用swap的空间,什么时候使用是由swappiness 参数值控制。v100@v100:~$ cat /proc/sys/vm/swappiness 60swappiness=0的时候表示最大限度使用物理内存,然后才是 swap空间,swappiness=100的时候表示积极的使用swap分区,并且把内存上的数据及时的搬运到swap空间里面。现在服务器的内存动不动就是上百G,所以我们可以把这个参数值设置的低一些,让操作系统尽可能的使用物理内存,降低系统对swap的使用,从而提高系统的性能。特别是对于性能要求较高的数据库服务器,要求全部使用内存。3.2 如何修改swappiness参数临时性修改:v100@v100:~$ sudo sysctl vm.swappiness=10 vm.swappiness = 10 v100@v100:~$ cat /proc/sys/vm/swappiness 10永久修改v100@v100:~$ sudo vim /etc/sysctl.conf # 在文件里添加如下参数 vm.swappiness=10 # 激活设置 sysctl -p参考资料https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/installation_guide/s2-diskpartrecommend-ppc#id4394007https://www.nowcoder.com/test/question/done?tid=49008240&qid=23231#summary
2022年01月13日
516 阅读
0 评论
0 点赞
2022-01-13
MySQL学习:SQL关联查询的七种JOIN
1.图示2.案例2.0 准备数据以一个简易问答系统为例,包括问题表和问题所属标签,问题表如下:CREATE TABLE `t_qa` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `title` varchar(200) NOT NULL DEFAULT '' COMMENT '标题', `answer_count` int(5) unsigned NOT NULL DEFAULT '0' COMMENT '回答个数', `label_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '标签id', `create_by` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '创建人', `create_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '创建时间', `update_by` bigint(20) unsigned DEFAULT NULL COMMENT '更新人', `update_date` datetime DEFAULT NULL COMMENT '更新时间', `del_flag` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '0:不删除,1:删除', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `t_qa` (`id`, `title`, `answer_count`, `label_id`, `create_by`, `create_date`, `update_by`, `update_date`, `del_flag`) VALUES (1, 'Java是什么?', 5, 1, 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (2, 'PHP是什么?', 4, 2, 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (3, '前端是什么?', 3, 3, 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (4, 'nodejs是什么?', 2, 0, 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (5, 'css是什么?', 1, 0, 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (6, 'JavaScript是什么?', 0, 0, 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0);标签表如下:CREATE TABLE `t_label` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称', `create_by` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '创建人', `create_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '创建时间', `update_by` bigint(20) unsigned DEFAULT NULL COMMENT '更新人', `update_date` datetime DEFAULT NULL COMMENT '更新时间', `del_flag` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '0:不删除,1:删除', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `t_label` (`id`, `name`, `create_by`, `create_date`, `update_by`, `update_date`, `del_flag`) VALUES (1, 'java', 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (2, 'php', 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (3, '大前端', 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (4, 'mybatis', 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (5, 'python', 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (6, '多线程', 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0);2.1 左连接(LEFT JOIN)问题回答个数标签id标签名称Java是什么?51javaPHP是什么?42php前端是什么?33大前端nodejs是什么?2NULLNULLcss是什么?1NULLNULLJavaScript是什么?1NULLNULLSELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq LEFT JOIN t_label tl ON tq.label_id = tl.id2.2 右连接(RIGHT JOIN)问题回答个数标签id标签名称Java是什么?51javaPHP是什么?42php前端是什么?33大前端NULLNULL4mybatisNULLNULL5pythonNULLNULL6多线程SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq RIGHT JOIN t_label tl ON tq.label_id = tl.id2.3 内连接(INNER JOIN)问题回答个数标签id标签名称Java是什么?51javaPHP是什么?42php前端是什么?33大前端SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq INNER JOIN t_label tl ON tq.label_id = tl.id2.4 左独有连接(LEFT JOIN)问题回答个数标签id标签名称nodejs是什么?2NULLNULLcss是什么?1NULLNULLJavaScript是什么?0NULLNULLSELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq LEFT JOIN t_label tl ON tq.label_id = tl.id WHERE tl.id IS NULL2.5 右独有连接(RIGHT JOIN)问题回答个数标签id标签名称NULLNULL4mybatisNULLNULL5pythonNULLNULL6多线程SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq RIGHT JOIN t_label tl ON tq.label_id = tl.id WHERE tq.label_id IS NULL2.6 全连接(FULL JOIN)由于MySQL不支持FULL OUTER JOIN,所以如果有全连接需求时,可用表达式:full outer join = left outer join UNION right outer join来实现。问题回答个数标签id标签名称Java是什么?51javaPHP是什么?42php前端是什么?33大前端nodejs是什么?2NULLNULLcss是什么?1NULLNULLJavaScript是什么?0NULLNULLNULLNULL4mybatisNULLNULL5pythonNULLNULL6多线程SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq LEFT JOIN t_label tl ON tq.label_id = tl.id UNION SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq RIGHT JOIN t_label tl ON tq.label_id = tl.id 2.7 全连接去交集(FULL JOIN)问题回答个数标签id标签名称nodejs是什么?2NULLNULLcss是什么?1NULLNULLJavaScript是什么?0NULLNULLNULLNULL4mybatisNULLNULL5pythonNULLNULL6多线程SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq LEFT JOIN t_label tl ON tq.label_id = tl.id WHERE tl.id IS NULL UNION SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq RIGHT JOIN t_label tl ON tq.label_id = tl.id WHERE tq.label_id IS NULL参考资料一张图看懂 SQL 的各种 JOIN 用法SQL七种JOIN解析【MySQL笔记】七种JOIN的SQL
2022年01月13日
870 阅读
0 评论
0 点赞
1
...
12
13
14
...
24