响应代码
1 | python复制代码import requests |
get方式的代码
1 | python复制代码import requests |
上面标准化后的数据的样子
1 | json复制代码{ |
post方式的代码
注意post方式中传递的参数是data,而get 是params
1 | python复制代码import requests |
标准化后的数据的样子
1 | json复制代码{ |
本文转载自: 掘金
开发者博客 – 科技是第一生产力
1 | python复制代码import requests |
1 | python复制代码import requests |
上面标准化后的数据的样子
1 | json复制代码{ |
注意post方式中传递的参数是data,而get 是params
1 | python复制代码import requests |
标准化后的数据的样子
1 | json复制代码{ |
本文转载自: 掘金
这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战
当某个类定义为final时,即这个类是不能有子类,final类中的所有方法都隐式为final,无法覆盖它们,所以在final类中给任何方法添加关键字是没有任何意义的。
1 | arduino复制代码final类型的类如何拓展? |
private final:类中所有private方法都隐式地指定为final的,由于无法取用private方法,所以也就不能覆盖它,对private方法增添final关键字,这样做并没有什么好处。
final方法可以被重载:父类的final方法是不能够被子类重写的,那么final方法可以被重载?可以的
Java允许在参数列表中以声明的方式将参数指明为final,这意味着无法在方法中更改参数引用所指向的对象,这个特性主要用来向匿名内部类传递数据。
所有的final修饰的字段都是编译期常量吗?
1 | arduino复制代码public class Test { |
k的值由随机数对象决定,所以不是所有的final修饰的字段都是编译期常量,只是k的值在被始化后无法被更改。
一个既是static又是final的字段只占据一段不能改变的存储空间,它必须在定义的时候进行赋值,否则编译器将不予通过。
1 | java复制代码import java.util.Random; |
执行结果输出:
1 | ini复制代码k=2 k2=7 |
static关键字所修饰的字段并不属于一个对象,而是属于这个类的,也可以理解为static final所修饰的字段仅占据内存的一个一份空间,一旦被初始化之后便不会被更改。
1 | csharp复制代码public class FinalDemo { |
假设线程A在执行writer()方法,线程B执行reader()方法。
写final域重排序规则:
写final域的重排序规则禁止对final域的写重排序到构造函数之外,这个规则的实现主要包含两个方面:
1、JMM禁止编译器把final域的写重排序到构造函数之外;
2、编译器会在final域写之后,构造函数return之前,插入一个storestore屏障。这个屏障可以禁止处理器把final域的写重排序到构造函数之外。
分析writer方法:
1、构造了一个FinalDemo对象;
2、把这个对象赋值给成员变量finalDemo.
image.png
由于a、b之间没有数据依赖性,普通域(普通变量)a可能会被重排序到构造函数之外,线程B就有可能读到的是普通变量a初始化之前的值(0),这样就可能出现错误。而final域变量b,根据重排序规则,会禁止final修饰的变量b重排序到构造函数之外,从而b能够正确赋值,线程B就能够读到final变量初始化后的值。
因此,写final域的重排序规则可以确保:在对象引用为任意线程可见之前,对象的final域已经被正确初始化过了,而普通域不具有这个保障。
读final域重排序规则:
读final域重排序规则为:在一个线程中,初次读对象引用和初次读该对象包含final域,JMM会禁止这两个操作的重排序,处理器会在读final域操作的前面插入一个LoadLoad屏障,实际上,读对象的引用和读该对象的final域存在间接依赖性,一般处理器不会重排序这两个操作。但是有一些处理器会重排序。因此,这条禁止重排序规则就是针对这些处理器而设定的。
read()方法主要包含三个操作:
1、初次读引用变量finalDemo;
2、初次读引用变量finalDemo的普通域a;
3、初次读引用变量finalDemo的final域b;
image.png
读对象的普通域被重排序到了读对象引用的前面就会出现线程B还未读到对象引用就在读取该对象的普通域变量,这显示是错误的操作。而final域的读操作就限定了在读final域变量前已经读到了该对象的引用,从而就可以避免这种情况。
读final域的重排序规则可以确保:在读一个对象的final域之前,一定会先读这个包含这个final域的对象的引用。
对final修饰的对象的成员域写操作:
针对引用数据类型,final域写针对编译器和处理器重排序增加了这样的约束:在构造函数内对一个final修饰的对象的成员域的写入,与随后在构造函数之外把这个被构造的对象的引用赋给一个引用变量,这两个操作是不能被重排序的。
1 | csharp复制代码public class FinalReferenceDemo { |
假如线程A执行writerOne方法,执行完后线程B执行writerTwo方法,然后线程C执行reader方法\
image.png
由于对final域的写禁止重排序到构造方法外,因此1和3不能被重排序。由于一个final域的引用对象的成员域写入不能与随后将这个被构造出来的对象赋给引用变量重排序,因此2和3不能重排序
对final修饰的对象的成员域读操作
JMM可以确保线程C至少能看到写线程A对final引用的对象的成员域的写入,即能看下arrays[0] = 1,而写线程B对数组元素的写入可能看到可能看不到。JMM不保证线程B的写入线程C可见,线程B 和线程C之间存在数据竞争,此时的结果是不可预知。如果可见的,可使用锁或者volatile.
本文转载自: 掘金
这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战
欢迎关注公众号OpenCoder,来和我做朋友吧~❤😘😁🐱🐉👀
鸡尾酒排序,是冒泡排序算法的一种升级。冒泡排序的每个元素都可以像气泡一样,根据自身大小,一点点的向着数组的某侧移动。算法每一轮都是从左到右来比较元素,进行单向的位置交换的。而鸡尾酒排序是在此基础上元素比较和交换过程变成了双向的。固鸡尾酒排序又称双向冒泡排序、鸡尾酒搅拌排序、搅拌排序、涟漪排序、来回排序或快乐小时排序, 是冒泡排序的一种变形。
鸡尾酒排序最糟或是平均所花费的次数都是O(n²),但如果序列在一开始已经大部分排序过的话,会接近O(n)。
现有一个数组,里面的数据为: [2,3,4,5,6,7,8,1],我们以此数据来分析:
冒泡排序过程如下:
元素 2、3、4、5、6、7、8已经是有序的了,只需要将元素1的放到正确的位置就可以了,却还是进行了7轮排序,这也太不方便了。要是能直接将1的位置进行调整,数列就有序了。鸡尾酒排序正是要解决这个问题的。
鸡尾酒详细过程:
第一轮(和冒泡排序一样,8和1交换)
第二轮:此时开始不一样了,我们反过来从右往左比较进行交换。
第三轮:虽然实际上已经有序,但是流程并没有结束。
在鸡尾酒排序的第三轮,需要重新从左向右比较进行交换。
1和2比较,位置不变;2和3比较,位置不变;3和4比较,位置不变…7和8比较位置不变。没有元素进行交换,证明当前有序,排序结束。
本来需要7轮的排序场景,用3轮就解决了,鸡尾酒排序就是这样巧妙的算法。
而鸡尾酒排序的思路,排序过程就像钟摆一样,第一轮从左往右比较,第二轮从右往左比较,第三轮再从左往右比较…
1 | java复制代码 public static void sort(int[] array) { |
结果输出:
1 | java复制代码第1次循环 |
这段代码是鸡尾酒排序的原始实现。代码外部的大循环控制所有排序回合,大循环内部包含两个小循环,第1个小循环从左往右比较并交换元素,第2个小循环从右往左比较并交换元素。
鸡尾酒的优势是,在大部分元素已经有序的情况下,减少排序的回合数;而缺点也很明显,就是代码量几乎翻了一倍。
欢迎关注公众号OpenCoder,来和我做朋友吧~❤😘😁🐱🐉👀
本文转载自: 掘金
「这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战」
hi ,大家好,我是三天打鱼,两天晒网的小六六
文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820…
种一棵树最好的时间是十年前,其次是现在
最近小六六业余时间都会刷点算法,至于原因当然是小六六太菜了,哈哈,是真的菜,不过刷了一些题后,发现自己的脑子不那么傻了,看来刷力扣还是有助于思维的提升,建议大家业余的时候刷刷,当然算法思维对于我们写的代码的性能也是有帮助的呢?刷力扣的过程中,经常会用到Arrays.sort这个方法,今天小六六就给大家分享分享这个方法,看看Java的JDK是怎么去做排序的
要了解Arrays.sort的底层原理,我们先来看看我们耳熟能详的排序算法吧,这边只是大概的提提这些算法,我们一般在开发的过程中,会碰到以下的排序算法
插入排序示意图
插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
算法步骤:
1)将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
2)从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
选择排序示意图
选择排序
(Selection sort)也是一种简单直观的排序算法。
算法步骤:
1)首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
2)再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
3)重复第二步,直到所有元素均排序完毕。
冒泡排序示意图
冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
算法步骤:
1)比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
3)针对所有的元素重复以上的步骤,除了最后一个。
4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
归并排序示意图
归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
算法步骤:
快速排序示意图
快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
算法步骤:
1 从数列中挑出一个元素,称为 “基准”(pivot),
2 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
当然还有其他的几种排序算法,大家也去了解下,但是我们常用的就上面5种了
1 | ini复制代码 |
1 | kotlin复制代码// Use Quicksort on small arrays |
1 | scss复制代码// Use insertion sort on tiny arrays |
1 | css复制代码/* |
1 | css复制代码 |
我们来看看Arrays.sort整一个流程图吧
O(nlogn)只代表增长量级,同一个量级前面的常数也可以不一样,不同数量下面的实际运算时间也可以不一样。
数量非常小的情况下(就像上面说到的,少于47的),插入排序等可能会比快速排序更快。 所以数组少于47的会进入插入排序。
快排数据越无序越快(加入随机化后基本不会退化),平均常数最小,不需要额外空间,不稳定排序。
归排速度稳定,常数比快排略大,需要额外空间,稳定排序。
所以大于或等于47或少于286会进入快排,而在大于或等于286后,会有个小动作:“// Check if the array is nearly sorted”。这里第一个作用是先梳理一下数据方便后续的双枢轴归并排序,第二个作用就是即便大于286,但在降序组太多的时候(被判断为没有结构的数据,The array is not highly structured,use Quicksort instead of merge sort.),要转回快速排序。
本文转载自: 掘金
C 语言 string 函数,在 C 语言中可以使用 char* 字符数组实现字符串,C 语言标准库 string.h 中也定义了多种字符串操作函数。
字符串使用广泛,需要满足:
下面代码展示了 C 语言中 ‘\0’ 结束字符对字符串的影响。下图展示了一个值为 “Redis” 的 C 字符串:
1 | c复制代码#include "stdio.h" |
输出结果是 3 和 5。
SDS(简单动态字符串) 是 simple dynamic string 的简称,Redis 使用 SDS 作为字符串的数据结构。Redis 中所有的键(key)底层都是 SDS 实现的。
比如:
1 | shell复制代码redis> SET msg "hello world" |
1 | bash复制代码redis> RPUSH fruits "apple" "banana" "cherry" |
Redis sds 源码主要在 sds.h 和 sds.c 中。其中可以发现 Redis 给 char* 起了别名:
1 | c复制代码typedef char *sds; |
SDS 结构中有一个元数据 flags,表示的是 SDS 类型(最低 3 位)。事实上,SDS 一共设计了 5 种类型,分别是 sdshdr5、sdshdr8、sdshdr16、sdshdr32 和 sdshdr64。这 5 种类型的主要区别就在于,它们数据结构中的字符数组现有长度 len 和分配空间长度 alloc,这两个元数据的数据类型不同。
1 | c复制代码/* Note: sdshdr5 is never used, we just access the flags byte directly. |
1 | c复制代码static inline size_t sdslen(const sds s) { |
获取剩余容量:sdsavail 函数,总容量 alloc - 已使用长度 len,时间复杂度是 O(1)。
1 | c复制代码static inline size_t sdsavail(const sds s) { |
基础方法有:
1 | c复制代码sds sdsnewlen(const void *init, size_t initlen); |
整体和 Java 的 StringBuilder 很像了 O_o
1 | c复制代码/* Create a new sds string starting from a null terminated C string. */ |
首先是判断输入的 init 字符串的长度,接着调用 sdsnewlen 分配内存空间并赋值。
1 | c复制代码sds sdsnewlen(const void *init, size_t initlen) { |
核心函数_sdsnewlen 如下,主要就是先确保空间是否足够、分配空间,然后再调用 memcpy 将 *init 复制到对应的内存空间。
1 | c复制代码/* Create a new sds string with the content specified by the 'init' pointer |
Java 编程思想-最全思维导图-GitHub 下载链接,需要的小伙伴可以自取~
原创不易,希望大家转载时请先联系我,并标注原文链接。
本文转载自: 掘金
在 GitHub 上找到并 fork Redis 源码 github.com/redis/redis,然后在本地 clone 自己 fork 出来的源码项目。这样更方便我们在学习源码的过程中,增加注释、调试等。
本人的技术栈是 Java,JetBrains 的重度用户,所以 IDE 也选用 JetBrains 的 CLion。官网地址是:www.jetbrains.com/clion/。
使用 Statistic 插件查看项目的整体情况。
看到 C 文件总共有 296 个文件,有效代码行数 12.4w 行。整体代码并不算多,抓住主流程框架学习之。
拿到源码先切换到 6.2 分支,整体编译一下。首先执行 make clean,接着执行 make,成功~
Java 编程思想-最全思维导图-GitHub 下载链接,需要的小伙伴可以自取~
原创不易,希望大家转载时请先联系我,并标注原文链接。
本文转载自: 掘金
设置了expire的key缓存过期了,但是服务器的内存还是会被占用,这是因为redis所基于的两种删除策略
redis有两种策略:
所以,虽然key过期了,但是只要没有被redis清理,那么其实内存还是会被占用着的。
内存占满了,可以使用硬盘,来保存,但是没意义,因为硬盘没有内存快,会影响redis性能。
所以,当内存占用满了以后,redis提供了一套缓存淘汰机制:MEMORY MANAGEMENT
maxmemory:当内存已使用率到达,则开始清理缓存
1 | markdown复制代码* noeviction:旧缓存永不过期,新缓存设置不了,返回错误 |
本文转载自: 掘金
「这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战」。
SpringBoot 中简化了大量的配置文件,取而代之的是利用注解完成之前通过配置文件完成的工作。操作上便捷了很多,但是也隐藏了一些内部实现细节,在使用的时候不能盲目,应该了解在以往 Spring 项目中是如何配置的,这样可以加深我们对 SpringBoot 的理解
类上加这个注解就说明这个类是一个配置类
在传统的 Spring 项目中,我们想要引入一个配置类,通常是定义一个
bean.xml,然后通过bean注解给容器中添加组件,注入到 Spring 容器中。
1 | xml复制代码<?xml version="1.0" encoding="UTF-8"?> |
在 SpringBoot 项目中,我们只需要创建一个配置类,然后在这个类上直接加上 @Configration 注解,这个时候,这个类就等同于之前创建的
bean.xml,之前是针对xml操作,这里我们直接操作配置类即可。通过@bean注解添加组件。当项目启动的时候就会自动检测到该类,然后将这个类加载注入到容器中,省略了我们之前手动配置注入,扫描配置文件的步骤。
1 | java复制代码@Configuration // 告诉SpringBoot 这是一个配置类 = bean.xml |
我们可以将容器中所有的组件名称打印出来
1 | java复制代码public static void main(String[] args) { |
可以看到,我们刚刚自己定义的已经注入到容器中了
并且这个组件实例是单例的,无论获取多少次都是一样的。
1 | java复制代码 // 获取组件 |
相比于
1.0版本,2.0版本 @Configuration 注解添加了新的属性proxyBeanMethods()。
这个属性的作用是,设置配置组件中获取组件实例对象的方式。简单理解为单例和多例的区别。默认为
true单例的。
当我们设置为
true的时候,我们获取到的配置对象(MyConfig)其实是一个代理对象(com.example.helloworlddemo.config.MyConfig$$EnhancerBySpringCGLIB$$97588a67@632aa1a3),每次我们调用这个对象去获取组件实例的时候,会先从容器中检查,有就拿容器内的,没有就新建一个。这样做的目的是为了保证容器中必然存在我们需要的实例对象,解决组件依赖的问题。想想如果一个组件依赖另一个组件, A 组件依赖 B 组件 ,我们获取 A 的时候容器内没有 B 那么这个时候就会出现空指针的问题。总之一句话,true的时候,SpringBoot 每次都会检查容器中是否存在我们需要的实例对象,保证实例存在。
当我们设置为
false的时候,返回的就是原始对象(com.example.helloworlddemo.config.MyConfig@c1a4620),这个时候每次我们调用获取实例方法都会返回一个新的实例对象,并且 SpringBoot 会跳过实例检查的步骤,所以这种方式下项目启动加载速度会更快,所以当我们的配置类仅仅是为了返回一个组件实例的时候,可以设置为false提高项目加载速度。
- 配置类里面使用
@Bean标注在方法上给容器注册组件,默认也是单实例的- 配置类本身也是组件
proxyBeanMethods:代理bean的方法
Full模式 :(proxyBeanMethods = true)
这种模式下,保证每个@Bean方法,被重复调用返回的组件都是单实例的,直接从容器中拿,没有就新建。Lite模式:(proxyBeanMethods = false)
这种模式下:每个@Bean方法 每次调用返回的实例对象都是新创建的- 组件存在依赖必须使用
Full模式默认。其他默认是否Lite模式*
本文转载自: 掘金
「这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战」
作者:汤圆
个人博客:javalover.cc
redo log 和 bin log 这两个日志系统,都是用来存储更新操作的,不过他们存储的方式不同;
redo log 主要存储做了哪些修改,比如把name=”javalover”改成 name=”admin”;
而bin log 不仅存储做了哪些修改,还存储修改的原始逻辑,比如update t set name=admin where id=1,会同时存储这条语句和name=”admin”最新值。
下面我们分别介绍下两者的工作流程和区别
redo log 重做日志,它属于存储引擎层,是InnoDB特有的的,MyISAM引擎不支持;
空间大小:
redo log 占用的空间大小是固定的,比如我们给redo log分配了4个文件,每个文件占用1G,那么redo log的固定大小就是4G;
当redo log 写满时,会执行合并操作,就是将redo log中的记录合并到数据库磁盘中;
这里也顺带引出了一个WAL的概念;
WAL机制:
全称为 write ahead logging,大致意思就是先写日志,再写磁盘,目的就是提高性能;
因为如果每一次的更新操作都要写进磁盘,那么就需要先把数据页从磁盘中取出来,然后更新,最后写到磁盘中;
这样一来,磁盘的随机IO成本会很高;
所以通过WAL机制,先把更新操作记录到redo log中,然后在系统空闲时,再合并到磁盘中,此时的合并是顺序写入的,磁盘的IO成本很低;
write_point 和 check_point:
write_point 写入点 和 check_point 擦除点,可以理解为两个指针,write_point负责指向待写入记录的位置,而check_point负责指向待擦除的位置;
可以把这两个点想象成铅笔和橡皮擦;
刚开始的时候,这两个指针都指的是redo log的起点,如下所示:
当记录了1条数据后,write_point就会往前移动1次,而check_point不动:
当记录写满后,无法继续写入,check_point就会往前移动,把记录合并到数据库中,然后清除掉该部分记录;
crash-safe:
这个crash-safe,指的是当MySQL服务异常重启时,之前提交的记录也不会丢失;
这个crash-safe是基于redo log实现的,而redo log又是InnoDB引擎特有的,所以很多时候我们都推荐用InnoDB引擎,因为更加安全;
bin log 归档日志,属于Service层的东西,跟存储引擎无关;
也就是说:bin log不仅支持InnoDB引擎,还支持MyISAM引擎;
既然有了redo log为啥还要有bin log呢?
其实是先有的bin log,后有的redo log;
刚开始的时候,MySQL的存储引擎只有MyISAM引擎,而MyISAM引擎只支持bin log,也就是归档日志,它并不具备crash-safe的能力;
后来有了InnoDB引擎,它支持redo log 重做日志,相应的也就有了crash-safe能力;
看起来好像bin log没啥用了,那为啥MySQL不直接取消bin log呢?
因为有的MySQL还是用的MyISAM引擎,取消掉bin log的话,那他们连最基本的日志记录都没有了;
上一篇我们有介绍change buffer,在更新操作时,会把更新记录存储到change buffer;
那change buffer和redo log有什么关系呢?
首先说下相同的地方:其实他俩的目的是一致的,都是为了减少磁盘的IO操作;
其次说下不同的地方:
其实总结下来就是,不管读还是写,都是先去change buffer中操作,然后根据情况再看要不要去redo log中操作;
这里我们以下面的语句为例子进行分析:这里假设系统用到了两个日志redo log和bin log
1 | sql复制代码update t set age=10 where id=1; |
这里面涉及到一个两阶段提交的概念;
两阶段提交:
就是对于redo log来说,不是一次就写入完成的,而是分两次;
第一次写入记录时设置为prepare状态,然后等待着bin log日志的写入;
等到bin log写入成功后,事务才会提交,此时redo log被引擎设置为commit状态;
这样做的目的就是保证事务的一致性,即redo log和bin log都拥有一致的更新记录;
上面基本都有涵盖到,这里用表格来说明一下,比较清晰:
| redo log | bin log | |
|---|---|---|
| 存储引擎 | InnoDB | InnoDB, MyISAM |
| MySQL架构层 | 存储引擎层 | Service服务层 |
| 存储方式 | 物理日志,存储做了哪些修改 | 逻辑日志,存储修改的逻辑语句以及结果 |
| 空间占用 | 大小固定,写满后需清空部分记录 | 可追加写入 |
redo log属于InnoDB引擎特有,支持crash-safe,也就是MySQL服务异常重启后,之前提交的数据记录还能找见;
bin log属于Service层,所有引擎都支持,大小不固定,可追加写入;
change buffer 和 redo log相辅相成:更新数据会先写入到change buffer,再写入redo log;查询数据先去change buffer找,找不到再去redo log找
本文转载自: 掘金
这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战
刚开始学习Mybatis可以先看下官方文档,MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis避免了几乎所有的JDBC代码和手工设置参数以及抽取结果集。MyBatis使用简单的XML或注解来配置和映射基本体,将接口和Java的POJOs(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
下面进入正题:
工具:Navicat premium 、IntelliJ IDEA
简单的目录结构
1.创建mysql数据库
创建一个firend_mq数据库,建立一张表为 users ,并插入一些数据
2.新建一个maven项目,并导入依赖
1 | xml复制代码 <dependencies> |
3.在resources文件夹下新建mybatis-config.xml,编写mybaits的核心配置文件
1 | xml复制代码<?xml version="1.0" encoding="UTF-8" ?> |
注意点:resource绑定mapper,需要使用路径,使用”/“
连接mysql数据库的时候可能会出现时区的问题,可以看这篇博客
IntelliJ IDEA连接Mysql数据库和出现的问题(最详细)
4.编写mybatis工具类
1 | java复制代码//SqlSessionFactory |
5.编写mybatis实体类
1 | java复制代码package pojo; |
前期的准备工作已完毕,开始编写代码
6.编写Dao层的接口
1 | java复制代码public interface UserDao { |
7.编写接口实现类
接口实现类由原来的UserDaoImpl转变为Mapper配置文件夹
1 | xml复制代码<!--namespace=绑定一个对应的Dao/Mapper接口--> |
注意点:
8.编写测试类
1 | java复制代码public class UserDaoText { |
这样一个简单Mybatis的增删改查就写完了,细节都在代码中由注释。
本文转载自: 掘金