C语言安全地释放指针

安全释放指针

最近在读有关C语言指针方面的书籍,看到有关malloc系列函数申请内存释放使用宏定义的方式。自己也动手试了一下,感觉还不错记录一下分享给小伙伴们。

实现和应用

#include <stdlib.h>
#include <stdio.h>

void safeFreePtr(void **p_ptr)
{
    if (p_ptr != NULL && *p_ptr != NULL)
    {
        free(*p_ptr);
        *p_ptr = NULL;
    }
}

/** 宏定义 */
#define safeFree(ptr)  safeFreePtr((void **)&(ptr))

int main(int argc, char const *argv[])
{
    int *a = (int *)malloc(sizeof(int));
    *a = 10;
    printf("Before: %p\n", a);
    safeFree(a);
    printf("After: %p\n", a);
    safeFree(a);
    return 0;
}

运行结果:

Before: 0x5634b2e342a0
After: (nil)

可以看出第二次safeFree时打印是:nil,程序没有出错。

对于申请的指针,使用free函数释放时只能free一次,第二次free会报错。

指针free释放后,一定要记得把指针置为NULL,防止悬挂指针。

好处

如果程序中是直接调用safeFreePtr函数是需要显示转换为 void** 指针,若不显示转换则会报警告。在使用宏调用safeFreePtr函数可以避免强制转换

查看系统默认内存对齐字节数

本文使用C语言查看系统默认内存对齐字节数。

#pragma pack(show)

实现当前系统默认对齐字节数,是以警告的方式显示

使用vscode编辑器,写入以下代码:

// 默认是8
#pragma pack(show)

int main(int argc, char const *argv[])
{
   return 0;
}

运行上面的代码后,会在vscode的控制台 【PROBLEMS】tab中显示下面的警告提示信息。

value of #pragma pack(show) == 8 [Semantic Issue]

表示当前内存对齐字节数8.

C基于链表实现栈

本篇内容中的链表是之前的文章中实现的,请参阅文章 C-单链表的实现

栈的特性

先进后出(First-In-Last-Out, FILO).

像队列实现一样,有2个主要的操作:

一)push 操作将元素入栈。

二)pop 操作将元素出栈。

栈的实现

基于单链表实现,push 入栈操作就是往链表的头部添加数据元素,pop 出栈操作就是从链表的头部删除数据元素

以下是栈的定义以及主要操作:

// 定义栈
typedef LinkedList Stack;
// 初始化栈
void initStack(Stack *stack)
{
    init_linkedlist(stack);
}
// 入栈,在链表头部添加元素
void push(Stack *stack, void *data)
{
    addHead(stack, data);
}
// 出栈,从链表头部删除元素
void* pop(Stack *stack)
{
    pNode node = stack->head;
    void* data;
    if (stack->head == NULL) {
        data = NULL;
    } else if (stack->head == stack->tail) {
        stack->head = stack->tail = NULL;
        data = node->data;
        free(node);
    } else {
        stack->head = node->next;
        data = node->data;
        free(node);
    }

    return data;
}

示例说明

    Stack stack;
    // 初始化
    initStack(&stack);
    int a = 20;
    int b = 30;
    int c = 40;
    int *ptr_1 = &a;
    int *ptr_2 = &b;
    int *ptr_3 = &c;

    push(&stack, ptr_1);
    push(&stack, ptr_2);
    push(&stack, ptr_3);

    int *ptr = NULL;
    for (int i = 0; i < 3; i++)
    {
        ptr = (int *)pop(&stack);
        printf("Popped %d\n", *ptr);
    }

运行结果:

Popped 40
Popped 30
Popped 20


以上内容如有错误,欢迎留言指出交流或者关注公众号交流。

C基于链表实现队列

本章内容是基于上一篇文章单链表的实现 C-单链表的实现

队列是先进先出(First in First out)的线性数据结构. 通常有2个主要的操作:enqueue 和 dequeue。

enqueue操作是将元素添加到列队中

dequeue操作是将元素从队列中移除

链表经常用于队列的实现。enqueue 操作是从链表的头部(head)添加元素,dequeue 操作是从链表的尾部(tail)移除元素。

队列定义

定义队列和相关操作

// 定义队列
typedef LinkedList Queue;
// 初始化队列
void initQueue(Queue *queue)
{
    init_linkedlist(queue);
}

// 添加元素
void enqueue(Queue *queue, void *data) {
    addHead(queue, data);
}

// 移除元素
void* dequeue(Queue *queue) 
{
    pNode node = queue->head;
    void* data;
    if (queue->head == NULL){
        data = NULL;
    } else if (queue->head == queue->tail) {
        queue->head = queue->tail = NULL;
        data = node->data;
        free(node);
    } else {
        // 找到倒数第二个节点,由于之前实现的链表是单链表,只能这么找
        while (node->next != queue->tail)
        {
            node = node->next;
        }
        queue->tail= node;
        node = node->next;
        queue->tail->next = NULL;

        data = node->data;
        free(node);
    }

    return data;   
}

示例说明

    Queue queue;
    // 初始化
    initQueue(&queue);
    int a = 20;
    int b = 30;
    int c = 40;
    int *ptr_1 = &a;
    int *ptr_2 = &b;
    int *ptr_3 = &c;

    enqueue(&queue, ptr_1);
    enqueue(&queue, ptr_2);
    enqueue(&queue, ptr_3);

    void *data = dequeue(&queue);
    printf("Dequeued %d\n", *((int*) data));

    data = dequeue(&queue);
    printf("Dequeued %d\n", *((int*) data));

    data = dequeue(&queue);
    printf("Dequeued %d\n", *((int*) data));

结构打印:

Dequeued 20
Dequeued 30
Dequeued 40


以上内容,如有错误,欢迎留言交流或添加公众号交流。

C-单链表的实现

本文将介绍使用C中关键字struct 定义结构体实现单链表。

在C语言中,结构体的自引用只能通过指针来实现,因为在系统中指针的大小是固定(32位系统时大小是4字节,64位系统时大小是8字节)。

结构体自引用实现方式

有2种实现方式,

方式一:

typedef struct tagNode
{
    char *pItem;
    struct tagNode *next;
} *pNode; // 注意结构体的结尾一定得有分号

方式二:

// typedef 一定要定义在struct之前
typedef struct tagNode *pNode; 
struct tageNode
{
    char *pItem;
    pNode next;
};

链表定义

定义链表和相关操作

// 定义一个用显示数据的函数指针
typedef void (*DISPLAY)(void*);
// 定义一个用于比较数据大小的函数指针
typedef int (*COMPARE)(void*, void*);

// 节点定义
typedef struct _node
{
    void *data; // 节点数据
    struct _node *next; // 下一个节点
} *pNode;

// 链表定义
typedef struct _linkedlist
{
    pNode *head;
    pNode *tail;
    pNode *current;
} LinkedList;

// 初始化链表
void init_linkedlist(LinkedList *list)
{
    list->head = NULL;
    list->tail = NULL;
    list->current = NULL;
}

// 向链表头添加数据节点
void addHead(LinkedList *list, void *data)
{
    pNode node = (pNode) malloc(sizeof(Node));
    node->data = data;
    if (list->head == NULL) {
        list->tail = node;
        node->next = NULL;
    } else {
        node->next = list->head;
    }
    list->head = node;
}

// 向链表尾部添加数据节点
void addTail(LinkedList *list, void *data)
{
    pNode node = (pNode) malloc(sizeof(Node));
    node->data = data;
    node->next = NULL;
    if (list->head == NULL) {
        list->head = node;
    } else {
        list->tail->next = node;
    }
    list->tail = node;
}

// 获取指定的节点信息
pNode getNode(LinkedList *list, COMPARE compare, void *data) 
{
    pNode node = list->head;
    while (node != NULL) {
        if (compare(node->data, data)) {
            return node;
        }
        node = node->next;
    }
    return NULL;
}
// 删除指定的节点
void deleteNode(LinkedList *list, pNode node)
{
    if (node == list->head)
    {
        if (list->head->next == NULL) {
            list->head = list->tail = NULL;
        } else {
            list->head = list->head->next;
        }
    } else {
        pNode tmp = list->head;
        while (tmp != NULL && tmp->next != node)
        {
            tmp = tmp->next;
        }
        if (tmp != NULL) {
            tmp->next = node->next;
        }
    }
    
    free(node);
}
// 展示数据
void displayList(LinkedList *list, DISPLAY display)
{
    printf("\nLinkedList shown:\n");
    pNode node = list->head;
    while (node != NULL) {
        display(node->data);
        node = node->next;
    }
}

// 比较函数
int compareInt(int *e1, int *e2)
{
    return *e1 == *e2;
}
// 显示数据
void displayIntVal(int *e)
{
    printf("%d\n", *e);
}

使用示例

1.从链表头部开始添加数据

    LinkedList linkedList;
    // 初始化
    init_linkedlist(&linkedList);
    int a = 20;
    int b = 30;
    int c = 40;
    int *ptr_1 = &a;
    int *ptr_2 = &b;
    int *ptr_3 = &c;

    addHead(&linkedList, ptr_1);
    addHead(&linkedList, ptr_2);
    addHead(&linkedList, ptr_3);

    displayList(&linkedList, (DISPLAY)displayIntVal);

打印结果:

LinkedList shown:
40
30
20

2.从链表尾部添加数据

    LinkedList linkedList;
    // 初始化
    init_linkedlist(&linkedList);
    int a = 20;
    int b = 30;
    int c = 40;
    int *ptr_1 = &a;
    int *ptr_2 = &b;
    int *ptr_3 = &c;

    addTail(&linkedList, ptr_1);
    addTail(&linkedList, ptr_2);
    addTail(&linkedList, ptr_3);

    displayList(&linkedList, (DISPLAY)displayIntVal);

打印结果:

LinkedList shown:
20
30
40

3.获取指定的节点

    LinkedList linkedList;
    // 初始化
    init_linkedlist(&linkedList);
    int a = 20;
    int b = 30;
    int c = 40;
    int *ptr_1 = &a;
    int *ptr_2 = &b;
    int *ptr_3 = &c;

    addTail(&linkedList, ptr_1);
    addTail(&linkedList, ptr_2);
    addTail(&linkedList, ptr_3);

    pNode node = getNode(&linkedList, (COMPARE) compareInt, ptr_2);
    printf("node val is %d\n", *((int *)node->data));

打印结果:

node val is 30

4.删除指定的节点

    LinkedList linkedList;
    // 初始化
    init_linkedlist(&linkedList);
    int a = 20;
    int b = 30;
    int c = 40;
    int *ptr_1 = &a;
    int *ptr_2 = &b;
    int *ptr_3 = &c;

    addTail(&linkedList, ptr_1);
    addTail(&linkedList, ptr_2);
    addTail(&linkedList, ptr_3);

    pNode node = getNode(&linkedList, (COMPARE) compareInt, ptr_2);
    printf("node val is %d\n", *((int *)node->data));

    deleteNode(&linkedList, node);
    displayList(&linkedList, (DISPLAY)displayIntVal);

打印结果:

node val is 30

LinkedList shown:
20
40


以上就是全部的内容,如有错误,欢迎留言交流或者关注公众号交流。

对C中const修饰指针的使用和理解

const用于定义常量类型,限定某些变量不允许修改。

const修饰指针时的原则:先忽略掉类型说明符,离谁近就修饰谁

1. 常量指针

常量指针:是指向常量的指针,指针指向地址中的内容是不可修改的,但是指针的指向是可以修改。

定义方式,以下2种都可以:

  1. const dataType *ptrName;
  2. dataType const *ptrName;
int a = 20;
int b = 30;
const int *ptr_i = &a;
// *ptr_i = 30; // 报错:read-only variable is not assignable 只读变量不可赋值
ptr_i = &b; // OK的

指针指向的内容不可修改,即:*ptrName(解引用时)表示的值不修改。

const修饰的是*ptr_i,所以不能修改的是就是 *ptr_i 的值。

2. 常指针(指针常量)

常指针:是指针类型的常量,即指针本身的值不可以修改,始终指向一个地址。因此常指针初始化需要给定一个地址值。但是指向地址的内容可以修改

定义方式,以下方式都是可以的(必须要初始化设置值):

  1. dataType *const ptrName;
  2. const *dataType ptrName;
int a = 20;
int *const ptr1 = &a;
//ptr1 = NULL; // 报错:cannot assign to variable 'ptr1' with const-qualified type 'int *const' 
*ptr1 = 30; // OK的

指针本身的值不可以修改,ptr1的值是&a,不可以修改,指针指向地址的内容可以修改即*ptr1(解引用时)表示的值可以修改。

const修饰的是ptr1,所以ptr1的值不能修改。

3. 常指针&常量指针的复合使用

int a = 30;
const int *const ptr_a = &a; 

指针的地址值和地址值的内容 都不可以修改。

第一个const 修饰的是 *ptr_a,所以 *ptr_a 的值不能修改。

第二个const 修饰的是 ptr_a,所以 ptr_a的值也不能修改。

4. 其他

const 常用于修饰函数的参数,防止函数内部修改外部变量。

对C中typedef的使用和理解

typedef声明为已存在的数据类型取一个别名,可以用于替换复杂的类型名。

typedef是存储类型说明符(typedef、auto、register、static、extern、_Thread_local),由于声明中只允许一个存储类型说明符,因此typedef不能和他的存储类型说明符一起使用

使用代码示例说明

// 为整型定义一个别名int32
typedef int int32;

// 为 int* 指针定义一个别名
typedef int* int_ptr;

// 1.为 char 定义一个类型别名 char_t
// 2.为 char* 定义一个类型别名 char_p(char类型指针)
// 3.为 char (*)(void) 定义一个类型别名 fptr(函数指针)
typedef char char_t, *char_p, (*fptr)(void);

// 定义可变长度数组
typedef int A[];
// 使用时确定具体长度,a的类型是int[2], b的类型是 int[3]
A a = {1, 2}, b = {3,4,5};

// 为 int[5] 定义一个别名为arr
typedef int arr[5];

// 为结构体取别名
// 1.为 _node 定义一个别名 Node
// 2.为 _node* 定义一个别名 P_Node (指针)
typedef struct _node
{
   void* data;
   struct _node *pNext;  // 结构体自引用
} Node, *P_Node;

// 为 tageNode* 定义一个别名 pNode
typedef struct tagNode *pNode;
struct tageNode
{
    char *pItem;
    pNode pNext; // 结构体自引用
};

注意:结构体的自引用只能通过指针来引用。

通过上面的代码和说明可以看出:把typedef和别名去掉后,剩余部分就是别名代表的数据类型

一点点之LocalDate

问题或建议,请公众号留言;
最新更新日期:2022-01-09

ISO 8601日期和时间格式

ISO 8601是国际通用的明确日期和时间格式的规范。该ISO标准有助于消除影响全球运营的各种日期约定、文化和时区可能导致的疑虑。它提供了一种显示日期和时间的方式,这种方式对人和机器来说都是明确定义和理解的。

LocalDate

LocalDateISO-8601日历系统中没有时区的日期。LocalDate是一个不可变的日期时间对象(线程安全的对象),它表示一个日期,通常展示视为【年-月-日】。
也可以访问其他的日期字段包含以下:

  • day-of-year

  • day-of-week

  • week-of-year

此类仅是对日期的描述,可用于生日,假期等。由于无关偏移量和时区,它就不能代表时间线上的一个瞬间。

比较相等是使用equals方法。
默认的格式是uuuu-MM-dd

LocalDate方法列表

方法名称 描述
now,of,from,parse,ofYearDay,ofEpochDay 这些静态方法根据指定条件生成一个LocalDate对象
plusDays,plusMonths,plusWeeks,plusYears 在当前日期上增加相应的天数,周数,月数,年数
minusDays,minusWeeks,minusMonths,minusYears 在当前日期上减少相应的天数,周数,月数,年数
plus,minus 在当前日期上 增加/减少 一个 DurationPeriod,或者ChronoUnit单位指定的值
withDayOfMonth,withDayOfYear,withMonth,withYear 以当前日期,修改为指定的日期,返回新的LocalDate对象
getDayOfMonth,getDayOfWeek,getDayOfYear 获取当前日期在月份中的第几天,周中的第几天,年中的第几天
getMonth,getMonthValue 获取当前日期所在的月份,前一个是枚举Month,后面的值是1-12
getYear 获取当前日期的年份,范围:-999999999+999999999
isAfter,isBefore 当前日期是否在指定日期之后或之前
isLeapYear 当前日期所在年份是否是闰年
lengthOfMonth,lengthOfYear 获取当前日期所在月份的天数,所在年份的天数
range 获取当前日期,指定ChronoField后的范围
until 获取2个日期之前的Period对象,或者ChronoUnit对应的数字
adjustInto,with 根据指定的条件返回调整后,新的日期.2个方法是等价的
atStartOfDay,atStartOfDay,atTime 获取带有时间的LocalDateTime

方法返回的LocalDateLocalDateTime都是新的对象,

与Calendar的不同

  1. 星期周的不同:Calendar中周日是1,周六是7。LocalDate默认的周一是1,周日是7。
  2. 月份的不同:Calendar是从0-11表示的。LocalDate中是从1-12表示的。

应用示例

LocalDate date = LocalDate.now();
System.out.println("date="+date);
System.out.println("是否为闰年:"+date.isLeapYear());
System.out.println("年中第几天:"+date.getDayOfYear());
System.out.println("月中第几天:"+date.getDayOfMonth());
System.out.println("周几:"+date.getDayOfWeek());
System.out.println("月份:"+date.getMonth());
System.out.println("月份值:"+date.getMonthValue());
System.out.println("加上3天:"+date.plusDays(3));
System.out.println("加上3天:"+date.plus(3, ChronoUnit.DAYS));
System.out.println("日期修改为1月30号:"+date.withDayOfMonth(30));
System.out.println("日期修改为3月份:"+date.withMonth(3));
System.out.println("日期修改为本年份的第50天:"+date.withDayOfYear(50));
System.out.println("当前月份的天数:"+date.lengthOfMonth());
System.out.println("当前年份的天数:"+date.lengthOfYear());
System.out.println("一周的范围值:"+date.range(ChronoField.DAY_OF_WEEK));
System.out.println("当前月天数的范围值:"+date.range(ChronoField.DAY_OF_MONTH));
System.out.println("当前年天数的范围值:"+date.range(ChronoField.DAY_OF_YEAR));
System.out.println("2个日期相差天数1:"+date.until(LocalDate.parse("2022-01-15"),ChronoUnit.DAYS));
System.out.println("2个日期相差天数2:"+ChronoUnit.DAYS.between(date,LocalDate.parse("2022-01-15")));
System.out.println("2个日期相差Period1:"+date.until(LocalDate.parse("2022-01-15")));
System.out.println("2个日期相差Period2:"+ Period.between(date,LocalDate.parse("2022-01-15")));

System.out.println("日期调整1:"+ date.with(temporal -> temporal.minus(Period.ofDays(2))));
System.out.println("日期调整2:"+ date.with(temporal -> temporal.minus(2, ChronoUnit.DAYS)));

LocalDateTime localDateTime = LocalDateTime.now();
System.out.println("当前日期时间:"+localDateTime);
System.out.println("日期调整3:"+ localDateTime.with(temporal -> temporal.minus(Duration.ofHours(2))));
System.out.println("日期调整4:"+ localDateTime.with(temporal -> temporal.minus(2, ChronoUnit.HOURS)));

运行结果:

date=2022-01-09
是否为闰年:false
年中第几天:9
月中第几天:9
周几:SUNDAY
月份:JANUARY
月份值:1
加上3天:2022-01-12
加上3天:2022-01-12
日期修改为1月30号:2022-01-30
日期修改为3月份:2022-03-09
日期修改为本年份的第50天:2022-02-19
当前月份的天数:31
当前年份的天数:365
一周的范围值:1 - 7
当前月天数的范围值:1 - 31
当前年天数的范围值:1 - 365
2个日期相差天数1:6
2个日期相差天数2:6
2个日期相差Period1:P6D
2个日期相差Period2:P6D
日期调整1:2022-01-07
日期调整2:2022-01-07
当前日期时间:2022-01-09T13:13:36.303
日期调整3:2022-01-09T11:13:36.303
日期调整2:2022-01-09T11:13:36.303

注意:Duration是基于时间的时间量,包含hours-mintues-seconds,也可以使用ChronoUnit.DAYS 单位等效于24小时 。

Period是基于日期的时间量,包含years-months-days.  ChronoUnit.YEARS / ChronoUnit.MONTHS  /  ChronoUnit.DAYS


结束。

来自博主的微信公众号。

MySQL的使用个人总结(一)

使用MySQL已经有一段时间了,对学习的知识来个总结吧。尽管知识有限还是向总结一下,好了,不多说了,一下是正文。

(个人比较喜欢在控制台操作,以下都是在控制台操作的)

       MySQL安装的时候,一般都会连文档一并安装到本地的,因此要好好运用这个文档吧。

win+r   cmd进入控制台。

控制台:   \h  可以查看所有的命令。

a.查看帮助主题(相当于目录)

控制台输入:   ?  contents

以上列表所有的分类。可以通过 ? categories名  查看每个categories的详细信息。

例如要查看  functions下面的文档内容,控制台输入: ? functions (大小写都是可以的)

要查看MySQL的相关文档,都是可以通过「? + 关键字」来查看的。

b.  show的使用

     ①查看DB中所有的数据库: show databases;

     ②选择一个数据库使用:  use databaseName

     ③查看使用数据库中的所有的表: show tables;

     ④查看一个表创建的详细内容,外键以及创建时的引擎: show create table tabName;

     ⑤查看一个表的所有字段: show columns from tabName from  databaseName 或者使用  show columns from  databaseName.tabName;

     ⑥查看使用数据库中每个表的信息: show table status \G;

     ⑦查看一个表的索引: show index from tabName  \G;

     ⑧查看可用的存储引擎: show engines \G;

     ⑨查看最后一个执行语句产生的错误消息: show errors;

     ⑩查看最后一个执行语句产生的错误,警告和通知: show warnings;

     十一查看一个用户的权限: show grants for root@localhost;

     十二查看系统变量的名称和值: show variables;

     十三查看系统特定资源信息: show status;

     十四服务器所支持的不同权限: show privileges;

     十五查看系统中正在运行的所有程序: show processlist;

     十六查看所有存储过程: show procedure status;

     十七查看所有存储过程: show function status;

     十八查看表中所有列的字符集 : show full columns from tb_name;

     十九查看mysql所支持的字符集 : show charset;

c. 查看MySQL的版本:   select version();

d. 查看db的当前的使用者:   select user();

e. tee的使用

        记录控制台上的所有操作: tee  d:\文件名.log

或者  \T  d:\文件名.log   (\T T一定要大写) (\h 可以查看)

        关闭tee 功能:  notee 或者   \t

f. 修改root用户的密码: ①UPDATE mysql.user SET password=PASSWORD(‘新密码’) WHERE User=’root’;FLUSH PRIVILEGES;②首先在DOS下进入目录mysql\bin,再运行一下代码: mysqladmin -u用户名 -p旧密码 password新密码

g. 用户权限

    1.–创建用户并授权,格式:grant 权限 on 数据库.* to 用户名@登录主机 identified by ‘密码’;

grant select on 数据库名.* to 用户名@登录主机 identified by ‘密码’;(只有select权限)

    2. 授权test用户拥有所有数据库的某些权限: @”%” 表示对所有非本地主机授权,不包括localhost。

grant select,delete,update,create,drop on *.* to test@”%” identified by “1234”;

    3. 对localhost授权(所有权限):

    grant all privileges on testDB.* to test@localhost identified by ‘1234’

    4. 若不想test有密码,可以再打一个命令将密码消掉。

grant select,insert,update,delete on mydb.* to test@localhost identified by ”;

    5. 只创建用户不授权

insert into mysql.user(Host,User,Password) values(“localhost”,”test”,password(“1234”));

所有的用户信息都在  mysql.user这个表中

    6. 撤销已经赋予给 dba用户权限的权限。

revoke all on *.* from dba@localhost;

    7. 删除用户

Delete FROM user Where User=’test’ and Host=’localhost’;flush privileges;

    8. 删除账户及权限

        drop user 用户名@’%’;

        drop user 用户名@localhost;

H.  导入.sql文件命令

    ①mysql> USE 数据库名;

        mysql> SOURCE d:/mysql.sql;

    ②在DOS状态进入目录\mysql\bin,然后键入以下命令:mysql -uroot -p密码 < d:/mysql.sql

I.  备份数据库(mysqldump命令在DOS的 mysql\bin 目录下执行,命令不以分号“;”结尾)

    1. 导出整个数据库: mysqldump -u用户名 -p密码 database_name > outfile_name.sql

    2. 导出一个表: mysqldump -u root -p test t_user > C:\Users\Desktop\outfile_name.sql(会提示输入密码)

    3. 导出一个数据库结构: mysqldump -u user_name -p -d –add-drop-table database_name > outfile_name.sql

 J. 将文本数据转到数据库中:

    1. 文本数据应符合的格式:字段数据之间用tab键隔开,null值用\n来代替.

        1 ry 大 2010-10-10

        2 uy 小 2010-12-23

    假设把这两组数据存为school.txt文件,放在c盘根目录下。

    2. 数据传入命令 : mysql> load data local infile “c:\school.txt” into table 表名;

K. 查询数据库中所有表名

mysql>select table_name from information_schema.tables where table_schema=’数据库名’ and table_type=’base table’;

    可以通过 show create table information_schema.tables \G;来查看tables的所有字段信息,来检索你所需要的字段。

——————————————————————————–

等再熟悉了,再写吧,常练习最重要。

Mybatis-Generator插件学习

本环境是spring boot+maven。
MBG(Mybatis Generator)的默认配置文件名:generatorConfig.xml,位置在:src/main/resources文件夹下,如果generatorConfig.xml没有放在resources下,请指定generatorConfig.xml配置文件的路径。

依赖:

1.Java8+
2.一个实现DatabaseMetaData接口的JDBC driver,主要方法getColumns 和getPrimaryKeys 是必须的。

注意点:
1.对于XLM-若已存在同名的XML文件,MBG则自动合并XML文件。且不会覆写你自定义的更改(自定义的SQL)。你可以一遍又一遍的运行它,不用担心XML内容的丢失与自定义更改。但是会替换先前运行生成的任何XML元素。
2.对于Java类-MBG不会合并Java文件。它会覆盖之前存在的文件或者使用新的唯一名称生成新文件。在迭代方式运行MBG时必须要手动合并Java文件。作为Eclipse插件时,MBG可以自动合并Java文件。
3.如果存在冲突,MBG将使用唯一名称(例如MyClass.java.1)保存新生成的文件。可以此命令覆盖存在的文件:java -jar mybatis-generator-core-x.x.x.jar -configfile \temp\generatorConfig.xml -overwrite

使用MySQL时注意事项:

1.MySQL支持signed和unsigned,numeric类型的字段。这些是JDBC不支持的,Mybatis Generator不能自动转换这些类型的字段。而Java类型都是signed的,使用unsigned时可能会导致无符号字段精度丢失。MBG为MySQL中的unsigned numeric类型的字段提供了<columnOverride>标签。

<table tableName="ALLTYPES" >
<columnOverride column="UNSIGNED_BIGINT_FIELD" javaType="java.lang.Object" jdbcType="LONG" />
</table>

必须自己将返回值转换为适当的类型(在这种情况下,为java.math.BigInteger)。一般是要转成范围更大的类型。

2.MySQL对Catalog和Schema不是友好支持。如果运行 create schema命令则会创建数据库,而JDBC驱动会将其报告为catalog.但是MySql语法不支持标准的catalog..table SQL语法。因此,最好不要在生成器配置中指定catalog或schema。 只需指定表名并在JDBC URL中指定数据库即可。如果使用Connector/J版本是8.x版本,则会尝试对mysql数据库中的系统表(sys, information_schema, performance_schema, etc.)生成代码。若要进制此行为,则 将 nullCatalogMeansCurrent=true追加到JDBC的URL中即可。

jdbc:mysql://127.0.0.1:3309/ssm?useUnicode=true&characterEncoding=utf-8&useSSL=false&nullCatalogMeansCurrent=true

一个数据库系统包含多个Catalog,每个Catalog包含多个Schema,每个Schema包含多个数据库对象(表、视图、字段等)
如数据库对象表的全限定名可表示为:Catalog名.Schema名.表名
SQL标准并不要求每个数据库对象的完全限定名称是唯一的,就象域名一样,如果喜欢的话,每个IP地址都可以拥有多个域名。

MBG插件包含一个goal目标即 mybatis-generator:generate,此目标maven不会自动执行,可以使用 mvn mybatis-generator:help 查看帮助。运行方法2种(也可以idea中双击mybatis-generator:generate):

1.命令行运行:mvn mybatis-generator:generate

2.覆写已存在的文件: mvn -Dmybatis.generator.overwrite=true mybatis-generator:generate

如果generatorConfig.xml文件使用了自定义的插件,在指定依赖前,请先编译,确定生成了jar包(不然会报错提示无法初始化该对象)

pom.xml文件中配置:

<build>
	<plugins>
		<plugin>
			<groupId>org.mybatis.generator</groupId>
			<artifactId>mybatis-generator-maven-plugin</artifactId>
			<version>1.4.0</version>
			<dependencies>
				<dependency>
					<groupId>mysql</groupId>
					<artifactId>mysql-connector-java</artifactId>
					<version>6.0.6</version>
				</dependency>
			</dependencies>
			<!-- 以下代码放开后则会作为maven构建的一部分,即随着maven的构建自动执行MBG,生产环境一定要注释 -->
			<!--<executions>
				<execution>
					<id>Generate MyBatis Artifacts</id>
					<goals>
						<goal>generate</goal>
					</goals>
				</execution>
			</executions>-->
		</plugin>
	</plugins>
</build>

generatorConfig.xml文件配置:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <property name="javaFileEncoding" value="UTF-8"/>

        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3309/ssm?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false"
                        userId="root" password="123456">
        </jdbcConnection>

        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <!-- 生成对象 -->
        <javaModelGenerator targetPackage="com.ruyi.mybatis.model" targetProject="src/main/java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- 生成xml文件 -->
        <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <!-- 生成mapper接口 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.ruyi.mybatis.mapper" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <table tableName="emp" domainObjectName="Employee">
            <property name="useActualColumnNames" value="true"/>
        </table>

        <table tableName="dept" domainObjectName="Department">
            <property name="useActualColumnNames" value="true"/>
        </table>
    </context>
</generatorConfiguration>
Copyright © 2023 蚕鸣夏季 . All rights reserved. 备案号:苏ICP备19001383号-1