Quantcast
Channel: CSDN博客推荐文章
Viewing all 35570 articles
Browse latest View live

Qml列表项拖放

$
0
0

ListModel的move(int from, int to, int n)
可以将列表项进行移动
根据鼠标的拖动位置, 可以判断出需要移动项的序号

                    var lastIndex = listview.indexAt(mousearea.mouseX + listItem.x,
                                                     mousearea.mouseY + listItem.y);
                    if ((lastIndex < 0) || (lastIndex > listModel.rowCount()))
                        return;
                    if (index !== lastIndex){
                        listModel.move(index, lastIndex, 1);
                    }
                    listItem.toIndex = lastIndex;

这里写图片描述

需要完整代码请访问QtQuickExamples

作者:zhengtianzuo06 发表于2017/11/25 15:10:47 原文链接
阅读:30 评论:0 查看评论

数独的Java版解法

$
0
0

最近偶尔有玩数独,有的题太复杂了不好解,刚好看到LeetCode上有这样的题,就尝试写了个Java的解法。

1. 数独介绍

数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。

左边是数独的题目,右边是完成后的结果。
数独题目 数独解法

2. 求解思路

2.1 方法定义与初始化

传入的是一个char[9][9]的数组,其中空白的部分用点号’.’代替,算出的解直接填进去即可。

public void solveSudoku(char[][] board)

我定义了一个Map<Integer, List<Character>> unsolve,它的key是当前未得到解的格子下标(从左上角开始到右下角分别是0-80),它的value是该格子可能的数值。

定义了一个初始化方法,用于初始化上面的那个map,即将每个未得到解的格子的可能数字填入1-9这9个数字。

private Map<Integer, List<Character>> initUnsolveMap(char[][] board) {
    Map<Integer, List<Character>> unsolve = new TreeMap<>();
    final List<Character> initChars = Arrays.asList('1', '2', '3', '4', '5', '6', '7', '8', '9');
    for (int y = 0; y < board.length; y++) {
        char[] chars = board[y];
        for (int x = 0; x < chars.length; x++) {
            char aChar = chars[x];
            if (aChar == '.') {
                unsolve.put(y * 9 + x, new ArrayList<>(initChars));
            }
        }
    }
    return unsolve;
}

当这个map的某个key的值只有一个元素时,那这个格子就确定了,我会将它从map中删除,并更新board。

数独最基本的解法是唯一余数法和摒除法。我采取的做法是用程序模拟人工去求解的办法。
下面开始正式求解过程。

2.1 唯一余数法

  1. 唯一余数法:用格位去找唯一可填数字,称为余数法,格位唯一可填数字称为唯余解。比如一行9个格子其中8个都填满了,那剩下的一个毫无疑问已经确定了。
  2. 我这里的做法是,依次遍历map的每个key,也就是每个未获得解的格子。
  3. 检查它所在的行、列、小方格,将不可能的数字剔除(见下面的代码)。例如所在行已经有2、5、7了,那这个格子肯定不会是这3个数字中的一个。这个步骤可以剔除不少数字。
  4. 可以看到代码中我定义了一个变量叫change,这个用于记录本轮执行过程中,map是否有发生变化。这个用途后面会说到。
  5. 对那些剔除后,只剩下一种可能数字的格子,进行移除操作,并更新board。
// 检查未解出的格子的每行、每列、每个方块,剔除已经出现的数字
for (Integer integer : unsolve.keySet()) {
    boolean thisChange = false;
    List<Character> resX = checkX(board, unsolve, integer);
    if (resX.size() > 0) {
        thisChange = true;
    }
    List<Character> resY = checkY(board, unsolve, integer);
    if (resY.size() > 0) {
        thisChange = true;
    }
    List<Character> resC = checkCube(board, unsolve, integer);
    if (resC.size() > 0) {
        thisChange = true;
    }
    if (thisChange) {
        change = true;
    }
}
// 对那些剔除后,只剩下一种可能数字的格子,进行移除操作,并更新board
List<Integer> needRemove = new ArrayList<>();
for (Integer integer : unsolve.keySet()) {
    List<Character> integers = unsolve.get(integer);
    if (integers.size() == 1) {
        board[integer / 9][integer % 9] = integers.get(0);
        needRemove.add(integer);
    }
    if (integers.size() == 0) {
        return FAIL;
    }
}
for (Integer integer : needRemove) {
    unsolve.remove(integer);
}

2.2 摒除法

  1. 摒除法:用数字去找单元内唯一可填空格,称为摒除法。例如9这个数字,在第一行和第二行都出现过了,那第三行的9一定不会在前2行出现过的宫格(9个格子的小方块)中,所以只剩下一个宫格的第三行会出现9,那如果那个宫格的那一行只有一个空位,那9就确定了。
  2. 与唯一余数法不同的是,这个时候可能该行、该列、该宫格都不止一个空位。这2种方法其实是使用不同的维度求解,一个从格子的维度,一个是从数字的维度,是互相补充的。一种方法无法继续求解后,可尝试第二种方法,就有可能求解了。
  3. 具体做法是,遍历1-9这9个数字,例如我们选择1这个数字进行说明
  4. 对1这个数字,遍历每一行的每个格子,如果该行没有1的话,那我们会检查剩下的格子的可能数字。
  5. 如果剩下的格子中,只有一个格子可能是1,那1就确认了。确认后进行删除map中的key并更新board。
//进行摒除法
for (char i = '1'; i <= '9'; i++) {
    boolean thisChange = checkNumX(board, unsolve, i);
    if (thisChange) change = true;
}

checkNumX方法:

private boolean checkNumX(char[][] board, Map<Integer, List<Character>> unsolve, char c) {
    boolean change = false;
    for (int y = 0; y < 9; y++) {
        boolean found = false;
        List<Integer> poss = new ArrayList<>();
        for (int x = 0; x < 9; x++) {
            if (board[y][x] == c) {
                found = true;
                break;
            }
            if (board[y][x] == '.') {
                int num = y * 9 + x;
                List<Character> characters = unsolve.get(num);
                if (characters.contains(c)) {
                    poss.add(num);
                }
            }
        }
        if (!found) {
            if (poss.size() == 1) {
                int integer = poss.get(0);
                board[integer / 9][integer % 9] = c;
                unsolve.remove(integer);
                change = true;
            }
        }
    }
    return change;
}

2.3 递归调用

  • 当使用上述方法求解后,可能board和unsolve这个map已经发生了变化(例如某个格子已经解出来了),那这个时候,重新进行上述两种方法,将可以排除新的数字。这也是我们前2个方法中有记录change变量的用处。
  • 在下面的代码中,我们判断change是否为true,也就是本轮是否发生了变化。如果发生了变化,我们进行新一轮求解。
  • 如果没有发生变化,有3种可能:
    • 已经全部解出来了,这个时候我们标记为SUCCESS
    • 前2种方法已经无法找出解了,这个时候我标记为UNDONE
    • 求解失败,当前无解,标记为FAIL
  • 这里我们用到了一个方法isValidSudoku,用来校验当前board的有效性,比较简单,就是检查一下每行、每列、每宫格有没有重复的数字,这里就不说了。
  • 当本轮change为true时,将重新递归调用本方法进行求解,直至某一轮未改变停止,将状态归为上面3种中的一种。
if (change) {
    return solve(board, unsolve);
} else {
    if (unsolve.size() > 0) {
        return UNDONE;
    } else {
        boolean valid = isValidSudoku(board);
        if (valid) {
            return SUCCESS;
        } else {
            return FAIL;
        }
    }
}

2.4 假设法

  1. 经过上面的递归尝试数独的2种基本求解方法,我发现这种方式,可以应对软件中的难度为简单和中等的数独题目,但是针对难度为困难的题目,就会卡住无法继续求解了(也就是状态为UNDONE)。
  2. 数独还有一些其他的复杂解法,例如区块摒除法)、数组、二链列、唯一矩形、全双值格致死解法、同数链、异数链及其他数链的高级技巧等等,这些解法代码层面实现起来较为复杂,故放弃。
  3. 这里我选择了比较适合代码编写的方式,也就是假设法,或者叫暴力求解法。也就是在上述方法得到的结果的基础上,找出那些可能数字比较少的一个格子。
  4. 针对这个格子,我分别设它为可能的每一个数字,并各自启动一个新的实例去求解。例如某个格子可能为3、5这2个数字,那我创建2个新的board,分别填入3、5这2个数字,并重新调用上面的求解办法。
  5. 按上面的方式继续求解,那会得到一个结果列表,里面可能有的是UNDONE,有的是FAIL和有的是SUCCESS,如果有SUCCESS,那我们的求解过程就结束了,将成功的board返回即可。而其中如果出现了FAIL,说明这个实例不可能存在,那就排除掉。
  6. 比较复杂的是出现UNDONE了,说明这一轮的假设仍然求不出解。那我们可以再继续假设。例如第一轮假设我们假设了3、5。然后返回了2个UNDONE,那我们还要继续在3的结果里面,继续假设,例如还是有2个可能性。这样我们可能就产生了4个假设的结果,再进行上面的判断。
  7. 说起来可能蛮复杂,但是代码实现起来挺简单的,也就是再次应用了递归的方法。如下所示:
private int recursion(char[][] rawBoard, Map<Integer, List<Character>> unsolve) {
    int status = solve(rawBoard, unsolve);//进行常规的求解过程,并获得当前求解的状态
    if (status == UNDONE) {//只有UNDONE的状态需要继续假设,SUCCESS和FAIL都不需要
        List<char[][]> boards = findPossBoard(rawBoard, unsolve);//根据当前结果进行假设,获得假设的board列表
        for (char[][] board : boards) {
            int newStatus = recursion(board, initUnsolveMap(board));//递归调用本方法,展开新的求解与假设过程
            if (newStatus == SUCCESS) {
                for (int i = 0; i < 9; i++) {
                    System.arraycopy(board[i], 0, rawBoard[i], 0, 9);//成功时,将成功的board赋值给原始的board数组。如果这不是最外层的递归,那将一层一层往上传递,直至传给最原始的board数组。
                }
                return SUCCESS;
            }
        }
        return FAIL;
    } else {
        return status;
    }
}

3. 总结

经过上面的4种方法共同求解,目前只要有解的数独都可以解出来。完整的代码见我的github:https://github.com/lnho/LeetCode_Java/blob/master/src/main/java/com/lnho/leetcode/solution/Solution037Simple.java ,里面还有一些测试的用例。

作者:Lnho2015 发表于2017/11/25 15:27:54 原文链接
阅读:81 评论:0 查看评论

KEIL调用GCC编译STM32

$
0
0

本篇记录KEIL调用GCC编译STM32

  1. 需要准备的工具有

    GCC编译器;我的版本5.4 下载地址https://launchpad.net/gcc-arm-embedded/

    KEIL;我的版本是5.23

    STM32对应的固件库;我的版本是F10x3.5

    一个已经配置好的STM32工程,led闪烁什么的都行,具体可参照正点原子的教程。

另外:版本问题不大,只需修改几个地方即可

  1. 解压GCC工具,编译ARM工具的名字应该是gcc-arm-none-eabi,解压到keil安装文件夹,可以随意放置,为了管理方便把他放在F:\Keil_v5\ARM\GCC,GCC文件夹是我新建的。如下图所示

  2. 在已经建立好的STM32工程中修改配置

    按照下图勾选Use GCC Compiler,并填入工具arm-none-eabi-和工具路径F:\Keil_v5\ARM\GCC\,然后点击确定

    接下来就是一些细节设置:

    首先是CC下的设置,Define中和原工程一样即可,中间全部勾选,然后是库文件的路径什么的和原工程一样,接着就是-ffunction-sections -fdata-sections,这个要加上去

     

    Linker中较为关键,首先是ld文件的路径.\Core\stm32_flash.ld,然后是

    -Wl,--gc-sections,将未调用的函数不编译进输出文件。接下来就说ld文件。

  3. 调用.ld文件、替换.S文件、修改core_cm3.c文件BUG

    1)调用.ld文件

.ld链接文件,里面规定了FLASH、RAM大小等信息,文件在固件库中STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template\TrueSTUDIO可找到,我选择的是10C里面的.ld文件。然后复制到工程下面,不能添加进工程,只在linker中做链接使用。打开文件第36行起修改相关信息

首先是RAM大小,然后是堆栈大小,FLASH等起始地址和长度,根据自己的实际情况修改。

/* Highest address of the user mode stack */

_estack = 0x20010000; /* end of 64K RAM */

 

/* Generate a link error if heap and stack don't fit into RAM */

_Min_Heap_Size = 0; /* required amount of heap */

_Min_Stack_Size = 0x200; /* required amount of stack */

 

/* Specify the memory areas */

MEMORY

{

FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K

RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K

MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K

}

2)替换.S文件

ARMCC和GCC所用的.S文件是不一样的,书写格式不一样,固件库STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\gcc_ride7中选择自己的.S文件并加入工程,替换掉原来的.S文件。

    3)修改core_cm3.c文件BUG

第736和753行分别修改为

__ASM volatile ("strexb %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value) );

__ASM volatile ("strexh %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value) );

 

 

闻道有先后,术业有专攻

作者:shaynerain 发表于2017/11/25 15:41:55 原文链接
阅读:27 评论:0 查看评论

OpenStack公共组件oslo之十——oslo.concurrency

$
0
0

        oslo.concurrency是一个为OpenStack其他项目提供用于管理线程的工具库,这样,OpenStack其他项目可以直接调用oslo.concurrency库利用其锁机制安全的运行多线程和多进程应用,也可以运行外部进程。本文总结了oslo.concurrency中常用的工具类或方法及其对应的使用方法。

1. lockutils

        lockutils模块封装了oslo库的锁机制,其中,定义了读写锁、信号量以及同步装饰器方法等。本节分别介绍这些类和方法的实现与使用。

1.1 锁机制

        lockutils中的锁机制实质上是直接使用了fasteners的实现,所以本节直接介绍fasteners库中的读写锁和共享内存,即ReaderWriteerLock和InterProcessLock类。

1.1.1 ReaderWriterLock类

        ReaderWriterLock类实现了一个读写锁,读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。fasteners通过ReadWriterLock类实现了读锁和写锁,其在该类中定义了READER和WRITER标识分别表示申请的锁是读锁还是写锁。使用该类可以获得多个读锁,但只能存在一个写锁。在目前的版本中,该类不能实现从读写升级到写锁,且写锁在加锁时不能获取读锁;而以后可能会对这些问题进行优化。该类的主要方法如下:

  • read_lock():为当前线程申请一个读锁,其只有在没有为其他线程分配写锁时才能获取成功;如果另一个线程以及获取了写锁,调用该方法会返回RuntimeError异常。
  • write_lock():为当前线程申请一个写锁,其只有在没有为其他线程分配读锁或写锁时才能获取成功;一旦申请写锁成功,将会阻塞申请读锁的线程。
  • is_reader():判断当前线程是否申请了读锁。
  • is_writer():判断当前线程是否申请了写锁,或已申请但尚未获得写锁。
  • owner():判断当前线程是否申请了锁;如果获得了锁是读锁还是写锁。
        ReaderWriterLock类中包含_writer属性表示占有写锁的线程,_readers属性表示占有读锁的线程集合,_pending_writers属性表示正在等待分配写锁的线程的队列,_current_thread属性表示当前线程,_cond属性表示threading.Condition对象。上述的这些方法就是通过操作这几个属性实现读写锁的。

1.2.1 InterProcessLock类

        InterProcessLock类是一个在POSIX系统上工作的进程间锁机制的实现类。该类会通过当前操作系统确定其内部的实现机制,通过该类可以实现多进程应用中共享内存、进程间通信与同步以及加锁等操作。其主要实现了tryLock()方法实现加锁,unlock()方法实现解锁,对于其具体实现,由于操作系统的不同其实现方法也不同,本人能力有限不再进行深入解析,有兴趣的同学可以自行研究。

1.2 信号量

        lockutils中也实现了信号量,准确来说是一个信号量垃圾收集的容器。这个集合在内部使用一个字典,这样当一个信号量不再被任何线程使用时,它将被垃圾收集器自动从这个容器中移除。其提供了一个get(name)方法,可以通过名称获取一个信号量。在具体使用时,可以直接lockutils中的internal_lock(name, semaphores=None)获取这个信号量容器。

1.3 同步装饰器

        lockutils中定义了两个同步装饰器方法synchronized(name, lock_file_prefix=None, external=False, lock_path=None, semaphores=None, delay=0.01)和synchronized_with_prefix(lock_file_prefix)。前者直接使用@synchronized(name)对装饰的方法加同步锁;而后者可以通过重新定义使用@synchronized(name)对装饰的方法加一个带有前缀的同步锁。

1.4 外部锁

        lockutils中也定义了两个方法分别用来获取和删除外部锁:external_lock(name, lock_file_prefix=None, lock_path=None)和remove_external_lock_file(name, lock_file_prefix=None, lock_path=None, semaphores=None)。其需要指定锁文件的前缀、锁文件路径以及锁的名称,通过这些属性,lockutils可以通过_get_lock_path(name, lock_file_prefix, lock_path=None)方法获取锁的位置,并根据锁文件创建和删除一个外部锁。

2 processutils

        processutils模块定义了一系列系统级的工具类和辅助函数。本小节将介绍processutils库的相关类或方法。

2.1 进程或线程异常类

        processutils模块中定义了多个进程或线程异常类,如参数不合法异常InvalidArgumentError、不知名参数异常UnknownArgumentError、进程执行异常ProcessExecutionError、日志记录异常LogErrors等。

2.2 资源限制类

        ProcessLimits类封装了一个进程对资源的限制,这些限制主要包括以下几个方面:

  • address_space:进程地址空间限制。
  • core_file_size:core文件大小限制。
  • cpu_time:CPU执行当前进程时间限制。
  • data_size:数据大小限制。
  • file_size:文件大小限制。
  • memory_locked:加锁的内存大小限制。
  • number_files:打开的文件最大数量限制。
  • number_processes:进程的最大数量限制。
  • resident_set_size:最大驻留集(RSS)大小限制。
  • stack_size:栈大小限制。

2.3 进程执行方法

        processutils模块中定义了执行进程的方法等,主要方法包括以下几个:
  • execute()方法:该方法通过启动一个子进程提取并执行一个命令。主要参数有:待执行的命令cmd;设置当前目录的cwd;发送到打开的进程process_input;为进程设置环境变量的env_variables;代表退出进程的int、bool或list值check_exit_code,默认为0,只有产生异常才会设置为其他值;重试延迟时间delay_on_retry,如果设置为True,表示马上进行重试操作;cmd重试次数attempts;run_as_root,该值如果设置为True,则为cmd命令加上root_helper指定的前缀;为命令指定的前缀root_helper;shell表示是否使用shell执行这个命令;执行命令记录日志的等级loglevel;监听错误日志log_errors,是一个LogErrors对象;binary,该值如果为True,则返回Unicode编码的stdout后stderr;prlimit表示一个ProcessLimits对象,用于限制执行该cmd的命令的资源用量。
  • trycmd()方法:execute()的一个装饰器,使用这个装饰器可以更加容易的处理错误和异常。返回一个包含命令输出strdout或stderr字符串的元组。如果err不为空,则表示执行命令出现异常或错误。
  • ssh_execute():通过ssh执行命令。
  • get_worker_count():获取默认的worker数量,返回CPU的数量;如果无法确定则返回1.

3 watchdog

        watchdog模块实现了一个看门狗程序,定义了一个watch()方法。如果操作执行时间超过阈值,则记录日志;此时可能发生了死锁或是一个耗时操作。其包含四个参数:
  • logger:一个记录日志的对象。
  • action:描述将执行的操作。
  • level:表示记录日志的等级,默认为debug。
  • after:发送消息之前的持续时间,默认为5s。

4 使用方法

        上文介绍了oslo.concurrency的各个模块的实现,接下来将详细介绍如何使用这些模块更好的管理OpenStack项目的线程或进程。
from oslo_concurrency import lockutils

@synchronized('mylock')
def foo(self, *args):
    ...

@synchronized('mylock')
def bar(self, *args):
    ...
        为一个方法添加@syschronized装饰器,可以保证统一时刻只有一个线程执行这个方法;但是,同时可以有两个方法共享这个锁,此时统一时刻要么只能执行foo方法,要么只能执行bar方法。
(in nova/utils.py)
from oslo_concurrency import lockutils

synchronized = lockutils.synchronized_with_prefix('nova-')


(in nova/foo.py)
from nova import utils

@utils.synchronized('mylock')
def bar(self, *args):
    ...
        如果需要设置一个带有前缀的同步锁,可以使用如上的方式进行设置。
        FORMAT = '%(asctime)-15s %(message)s'
        logging.basicConfig(format=FORMAT)
        LOG = logging.getLogger('mylogger')

        with watchdog.watch(LOG, "subprocess call", logging.ERROR):
            subprocess.call("sleep 10", shell=True)
            print "done"
        如果设置一个看门狗,则可以使用with语法调用watchdog.watch()方法。

作者:Bill_Xiang_ 发表于2017/11/25 15:57:23 原文链接
阅读:3 评论:0 查看评论

C#生成的应用程序版本如何修改

$
0
0

C#生成的应用程序右键详细信息上面的版本到底怎么改呢,一直想知道。


今天特地查了下,原来在这里,在Propertise下的Assemblyinfo.cs中


双击打开后就可看到相关信息

using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;

// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("WpfApplication2")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WpfApplication2")]
[assembly: AssemblyCopyright("Copyright ©  2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

//将 ComVisible 设置为 false 将使此程序集中的类型
//对 COM 组件不可见。  如果需要从 COM 访问此程序集中的类型,
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]

//若要开始生成可本地化的应用程序,请
//<PropertyGroup> 中的 .csproj 文件中
//例如,如果您在源文件中使用的是美国英语,
//使用的是美国英语,请将 <UICulture> 设置为 en-US。  然后取消
//对以下 NeutralResourceLanguage 特性的注释。  更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。

//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]


[assembly: ThemeInfo(
    ResourceDictionaryLocation.None, //主题特定资源词典所处位置
                                     //(当资源未在页面
                                     //或应用程序资源字典中找到时使用)
    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
                                              //(当资源未在页面
                                              //、应用程序或任何主题专用资源字典中找到时使用)
)]


// 程序集的版本信息由下列四个值组成: 
//
//      主版本
//      次版本
//      生成号
//      修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
// 方法是按如下所示使用“*”: :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]




作者:shaynerain 发表于2017/11/25 16:01:51 原文链接
阅读:14 评论:0 查看评论

应用程序打包神器Inno setup

$
0
0

使用方法自行百度,十分简单,一直下一步下一步。

问题合集,来自http://blog.163.com/53_54/blog/static/6013812200710179158720/

【问题一:Inno Setup 执行REG文件代码?】
[Run]
Filename: "{win}\regedit.exe";Parameters:"/s {tmp}\reg.reg" // 静默参数/S


【问题二:安装时,如果已经有同名文件存在,就不更新该文件?】
[Files]
Source: "test.tmp"; DestDir: "{app}"; Flags: onlyifdoesntexist  //onlyifdoesntexist表示只有当这个文件不存在的时候才安装这个文件。


【问题三:卸载时不卸载某一个文件?】
[Files] 
Source: "CTL3DV2.DLL"; DestDir: "{sys}"; Flags: uninsneveruninstall    //uninsneveruninstall 卸载时,不删除


【问题四:Inno Setup 注册OCX】
[Files]
Source: "xxx\xxx.ocx"; DestDir: "{app}"; Flags: onlyifdoesntexist regserver  //注册regserver


【问题五:如何在Inno Setup中设置某选项默认被选择?】
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkedonce //参数Flags包含checkedonce即可


【问题六:Inno Setup 安装新版本之前卸载老版本?】
[code]
if RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\My_Program_is1', 'UninstallString', ResultStr) then
begin
  ResultStr := RemoveQuotes(ResultStr);
  Exec(ResultStr, '', '', SW_SHOWNORMAL, ewWaitUntilTerminated, ErrorCode)
end

把其中 My_Program 换成自己的 app name 即可!


Flags :该参数是一个额外的可选项,可用空格隔开多个选项。下面是该参数所支持的选项: 
createvalueifdoesntexist :当指定该标记时,安装程序只会在同名键值不存在时创建该键值。该标记在数据类型为none或者指定了deletevalue标记时不会生效。
deletekey :当指定该标记时,安装程序将会先删除整个键(若存在的话),包括其中的所有键值和子键。如果ValueType 为none时,它才会创建新的键和键值。
deletevalue :当指定该标记时,安装程序会先删除这个键值(若存在的话)。如果ValueType 为none并且不曾存在时,它才会创建新的键值。 
dontcreatekey :当指定该标记时,如果指定的键在用户的系统中未曾存在时,安装程序不会尝试去创建指定的键或键值。如果指定的键已经存在,将不显示错误信息。典型的例子就是该标记与uninsdeletekey标记合用,以此达到在卸载时删除键而在安装时不会创建它们。
noerror :无论任何原因而导致安装程序创建该键失败都不会显示错误信息。 
preservestringtype :这只在ValueType参数是string或expandsz时使用。当指定了该标记后,如果键值不存在并且它是字符串类型时,它将被换成先前值的相同类型。
uninsclearvalue :卸载程序时,清空键值的数据(REG_SZ类型)。该标记不能与uninsdeletekey标记合用。
uninsdeletekey :卸载程序时,删除整个键,包括其中所有的键值和子键。显然,在Windows自身的键内时,这不是一个好主意。你只应在你软件的私有键中使用它。
uninsdeletekeyifempty :卸载程序时,如果该键没有键值或子键时将其删除。该标记可以与uninsdeletevalue.标记合用。
uninsdeletevalue :卸载程序时删除键值。该标记可以与uninsdeletekeyifempty标记合用。
作者:shaynerain 发表于2017/11/25 16:07:50 原文链接
阅读:24 评论:0 查看评论

十.ARM裸机学习之中断系统(S5PV210的按键和中断系统详解)

$
0
0
一.轮询方式处理按键
参考 E:\Linux\8.key_open
二.中断方式处理按键 2017/11/21 23:30
1.异常向量表分析:

    (1)、复位(RESET)
    a、当处理器复位引脚有效时,系统产生复位异常中断,程序跳转到复位异常中断处理程序处执行,包括系统加电和系统复位。
    b、通过设置PC跳转到复位中断向量处执行称为软复位。
    (2)、未定义的指令
    当ARM处理器或者是系统中的协处理器认为当前指令未定义时,产生未定义的指令异常中断,可以通过改异常中断机制仿真浮点向量运算。
    (3)、软件中断
    这是一个由用户定义的中断指令(SWI)。该异常由执行SWI指令产生,可用于用户模式下的程序调用特权操作指令。在实时操作系统中可以通过该机制实现系统功能调用。
    (4)、指令与取终止(Prefech Abort)
    如果处理器预取的指令的地址不存在,或者该地址不允许当前指令访问,当被预取的指令执行时,处理器产生指令预取终止异常中断。
    (5)、数据访问终止(DATAABORT)
    如果数据访问指令的目标地址不存在,或者该地址不允许当前指令访问,处理器产生数据访问终止异常中断。
    (6)、外部中断请求(IRQ)
    当处理器的外部中断请求引脚有效,而且CPSR的寄存器的I控制位被清除时,处理器产生外部中断请求异常中断。系统中个外设通过该异常中断请求处理服务。
    (7)、快速中断请求(FIQ)
    当处理器的外部快速中断请求引脚有效,而且CPSR的F控制位被清除时,处理器产生外部中断请求异常中断。
一.CPU自动处理的过程包括如下:
1、  拷贝CPSRSPSR_
2、  设置适当的CPSR位: 改变处理器状态进入ARM状态;改变处理器模式进入相应的异常模式;设置中断禁止位禁止相应中断。
3、  更新LR_,这个寄存器中保存的是异常返回时的链接地址
4、  设置PC到相应的异常向量
异常中断处理返回
 二. 异常处理完毕之后,ARM微处理器会执行以下几步操作从异常返回:
    (1)、将所有修改过的用户寄存器从处理程序的保护栈中恢复。 
    (2)、将SPSR复制回CPSR中,将连接寄存器LR的值减去相应的偏移量后送到PC中。 
    (3)、若在进入异常处理时设置了中断禁止位,要在此清除。
    复位异常处理程序不需要返回。
当异常发生时,内核会跳转到向量表的相应位置,并执行当中的指令。对于每一种异常,保存的返回地址都是不一样的,一般都需要我们手动的跳转,当然调整的时机也需要我们选择,是在进入处理前跳转还是返回时调整都是需要我们程序员控制的。

三.中断处理的第一阶段(异常向量表阶段)处理。 2017/11/23 22:35
第一个阶段的主要任务是从异常发生到响应异常并且保存/恢复现场、跳转到真正的异常处理程序处
1.查210的iROM application ,iRAM中的异常向量表起始地址为0xD0037400

各个异常对应的入口就等于异常向量表的起始地址+上图中入口的偏移地址:如下图

2.中断处理保存现场,包括:第一:设置IRQ栈;第二,保存LR;第三,保存R0~R12,


1)中断处理要注意保护现场(中断从SVC模式来,则保存SVC模式下的必要寄存器的值)和恢复现场(中断处理完成后,准备返回SVC模式前,要将保存的SVC模式下的必要寄存器的值恢复回去,不然到了SVC模式后寄存器的值乱了,SVC模式下原来正在进行的常规任务就被你搞坏了)
(2)保存现场包括:第一:设置IRQ栈;第二,保存LR;第三,保存R0~R12
(3)为什么要保存LR寄存器?要考虑中断返回的问题。中断ISR执行完后如何返回SVC模式下去接着执行原来的代码。中断返回其实取决于我们进入中断时如何保存现场。中断返回时关键的2个寄存器就是PC和CPSR。所以我们在进入IRQ模式时,应该将SVC模式下的下一句指令的地址(中断返回地址)和CPSR保存起来,将来恢复时才可以将中断返回地址给PC,将保存的CPSR给CPSR。
(4)中断返回地址就保存在LR中,而CPSR(自动)保存在(IRQ模式下的)SPSR中

四、中断处理的第二阶段处理过程
第二个阶段就是进入了真正的异常处理程序irq_handler之后,目的是识别多个中断源中究竟哪一个发生了中断,然后调用相应的中断处理程序来处理这个中断。

1.第一阶段:找到具体是哪个中断:S5PV210中因为支持的中断源很多,所以直接设计了4个中断寄存器,每个32位,每位对应一个中断源。(理论上210最多支持128个中断,实际支持不足128个,有些位是空的);210没有子中断寄存器,每个中断源都是并列的。当中断发生时,在irq_handler中依次去查询4个中断源寄存器,看哪一个的哪一位被置1,则这个位对应的寄存器就发生了中断,即找到了中断编号
2.第二阶段找到对应的中断服务处理isr:2210开拓了一种全新的寻找isr的机制。210提供了很多寄存器来解决每个中断源对应isr的寻找问题,实现的效果是当发生相应中断时,硬件会自动的将相应isr推入一定的寄存器中,程序员在设置中断的时候,把这个中断的isr地址直接放入这个中断对应的VECTADDR寄存器即可。

S5PV210中断处理的主要寄存器:
VIC0INTENABLE:中断使能
VIC0INTENCLEAR:中断禁止
VIC0INTSELECT:中断选择,IRQ/FIQ
VIC0IRQSTATUS:IRQ中断状态寄存器,软件在处理中断第二阶段的第一阶段,就是靠查询这个寄存器来得到中断编号
VIC0FIQSTATUS:FIQ中断状态寄存器
VIC0VECTADDR:中断源对应的ISR地址,中断发生第二阶段如何获取isr地址由这个寄存器自动完成,只需要拿出ISR地址,调用即可
VICnVECTPRIORITY31:中断优先级设置寄存器
VIC0ADDRESS:向量地址寄存器,当有中断发生时,硬件上会将当前中断的中断服务例程ISR地址从寄存器VICVECTADDR自动拷贝到寄存器VICDDR中
总结S5PV210的中断体系采用如下的ISR确定策略
(1)、将128个中断源分为4组,每组的ISR组成一个数组,以中断号为数组索引。
(2)、4组ISR数组的首地址分别存放在VICVECTADDR0~VICVECTADDR3中。
绑定ISR时,只需将用户自己编写的ISR地址放入ISR数组中以中断号为索引的位置即可

五、中断处理流程(参考:http://blog.51cto.com/9291927/1787523) 2017/11/24 22:51
S5PV210中断处理流程:
1、中断处理前的准备工作
A、初始化中断控制器。
外部中断的中断控制器的初始化包括:绑定第一阶段异常向量表;外设相应GPIO引脚设置为外部中断模式、外部中断控制寄存器的初始化、中断向量控制寄存器的初始化。
内部中断的中断控制器的初始化包括:中断向量控制寄存器的初始化,
B、绑定中断源的ISR到向量中断控制器的VICnVECTADDR。
C、使能中断源中断。
问题1:如何绑定自己实现的isr到VICnVECTADDR
(1)搞清楚2个寄存器的区别:VICnVECTADDR和VICnADDR(寄存器在上面已经解释
(1)VICVECTADDR寄存器一共有4×32个,每个中断源都有一个VECTADDR寄存器,我们应该将自己为这个中断源写的isr地址丢到这个中断源对应的VECTADDR寄存器中即可。

问题二:真正的中断处理程序如何获取isr?
(1)当发生中断时,硬件会自动把相应中断源的isr地址从VICnVECTADDR寄存器中推入VICnADDR寄存器中,所以我们第二阶段的第二阶段isr_handler中,只需要到相应的VICnADDR中去拿出isr地址,调用执行即可。

2、中断处理流程
A、异常向量表跳转到IRQ入口
CPU处理常规任务时收到中断的通知,于是S5PV210根据iROM固化代码跳转到iRAM的异常向量表的IRQ异常向量地址。一般将负责保护现场、处理中断、恢复现场的汇编代码段的起始地址赋值给IRQ异常向量地址,作为处理中断的入口。
B、保护中断现场(在start.S中),然后跳入isr_handler
处理中断之前先要保护总线 现场,主要工作是:设置IRQ模式下的栈,将R0-R12,LR压栈保存(由于流水线的原因,LR一般为当前执行的指令地址加8,LR减去4就是下一条要运行指令的地址)。
C、执行中断处理程序
在isr_handler中先去搞清楚是哪个VIC中断了。查看VICnADDR寄存器是否有ISR函数地址,如果有,执行ISR。一般在中断发生之前已经绑定好ISR到VICnVECTADDR。
D、恢复现场
执行ISR后,将栈中的R0-R12,LR分别出栈R0-R12,PC,程序将根据PC的值自动跳转会到常规任务执行。

S5PV210中断处理
1.S5PV210的外部中断通过外部中断对应的GPIO产生
外部中断寄存器位于文档中GPIO部分。
外部中断的主要寄存器:
EXT_INT_N_CON:设置外部中断的触发方式
EXT_INT_N_PEND:中断挂起寄存器,32位,每位对应一个外部中断源,
EXT_INT_N_MASK:中断掩码控制寄存器,各个外部中断的使能/禁止开关。

分析X210开发板的按键对应的EINT编号:
GPIO :SW5:GPH0_2 SW6:GPH0_3 SW78910:GPH2_0123
EINT :EINT2、EINT3、EINT16、EINT17、EINT18、EINT19

2.中断方式处理按键编程
  • 按键的初始化(包括外部中断的GPIO,触发模式等设置,分开说明)
2.1. 外部中断对应的GPIO模式设置
rGPH0CON |= 0xFF<<8;		// GPH0_2 GPH0_3设置为外部中断模式
rGPH2CON |= 0xFFFF<<0;// GPH2_0123共4个引脚设置为外部中断模式


.
2.2. 中断触发模式设置
	rEXT_INT_0_CON &= ~(0xFF<<8);	// bit8~bit15全部清零
	rEXT_INT_0_CON |= ((2<<8)|(2<<12));		// EXT_INT2和EXT_INT3设置为下降沿触发
	rEXT_INT_2_CON &= ~(0xFFFF<<0);       //EINT16、17、18、19,设置为下降沿触发
        rEXT_INT_2_CON |= ((2<<0)|(2<<4)|(2<<8)|(2<<12));	


按键的初始化(包括外部中断的GPIO,触发模式等设置)的整合如下:
2.3. 中断允许
	rEXT_INT_0_MASK &= ~(3<<2);			// 外部中断允许
	rEXT_INT_2_MASK &= ~(0x0f<<0);


2.4. 清挂起,清除是写1,不是写0
	rEXT_INT_0_PEND |= (3<<2);
	rEXT_INT_2_PEND |= (0x0F<<0);


按键的初始化(包括外部中断的GPIO,触发模式等设置)的整合如下:

3.中断处理程序isr编写及其整体调用
3.1.中断ISR处理函数的编写,即(类似于STM32的中断服务函数)


3.2.绑定isr到中断控制器硬件


3.3.使能中断(不是使能外部中断(因前面的中断初始化已经使能),是使能中断控制器)

3.4.整体调用如下:

3.5最后,上面的理解在结合下图 一起理解,就显得很简单了。
中断处理的编程模型:




int.c:
#include "int.h"
#include "stdio.h"


void reset_exception(void)
{
	printf("reset_exception.\n");
}

void undef_exception(void)
{
	printf("undef_exception.\n");
}

void sotf_int_exception(void)
{
	printf("sotf_int_exception.\n");
}

void prefetch_exception(void)
{
	printf("prefetch_exception.\n");
}

void data_exception(void)
{
	printf("data_exception.\n");
}

// 主要功能:绑定第一阶段异常向量表;禁止所有中断;选择所有中断类型为IRQ;
// 清除VICnADDR为0
void system_init_exception(void)
{
	// 第一阶段处理,绑定异常向量表
	r_exception_reset = (unsigned int)reset_exception;
	r_exception_undef = (unsigned int)undef_exception;
	r_exception_sotf_int = (unsigned int)sotf_int_exception;
	r_exception_prefetch = (unsigned int)prefetch_exception;
	r_exception_data = (unsigned int)data_exception;
	r_exception_irq = (unsigned int)IRQ_handle;
	r_exception_fiq = (unsigned int)IRQ_handle;
	
	// 初始化中断控制器的基本寄存器
	intc_init();
}

// 清除需要处理的中断的中断处理函数的地址
void intc_clearvectaddr(void)
{
    // VICxADDR:当前正在处理的中断的中断处理函数的地址
    VIC0ADDR = 0;
    VIC1ADDR = 0;
    VIC2ADDR = 0;
    VIC3ADDR = 0;
}

// 初始化中断控制器
void intc_init(void)
{
    // 禁止所有中断
	// 为什么在中断初始化之初要禁止所有中断?
	// 因为中断一旦打开,因为外部或者硬件自己的原因产生中断后一定就会寻找isr
	// 而我们可能认为自己用不到这个中断就没有提供isr,这时它自动拿到的就是乱码
	// 则程序很可能跑飞,所以不用的中断一定要关掉。
	// 一般的做法是先全部关掉,然后再逐一打开自己感兴趣的中断。一旦打开就必须
	// 给这个中断提供相应的isr并绑定好。
    VIC0INTENCLEAR = 0xffffffff;
    VIC1INTENCLEAR = 0xffffffff;
    VIC2INTENCLEAR = 0xffffffff;
    VIC3INTENCLEAR = 0xffffffff;

    // 选择中断类型为IRQ
    VIC0INTSELECT = 0x0;
    VIC1INTSELECT = 0x0;
    VIC2INTSELECT = 0x0;
    VIC3INTSELECT = 0x0;

    // 清VICxADDR
    intc_clearvectaddr();
}


// 绑定我们写的isr到VICnVECTADDR寄存器
// 绑定过之后我们就把isr地址交给硬件了,剩下的我们不用管了,硬件自己会处理
// 等发生相应中断的时候,我们直接到相应的VICnADDR中去取isr地址即可。
// 参数:intnum是int.h定义的物理中断号,handler是函数指针,就是我们写的isr

// VIC0VECTADDR定义为VIC0VECTADDR0寄存器的地址,就相当于是VIC0VECTADDR0~31这个
// 数组(这个数组就是一个函数指针数组)的首地址,然后具体计算每一个中断的时候
// 只需要首地址+偏移量即可。
void intc_setvectaddr(unsigned long intnum, void (*handler)(void))
{
    //VIC0
    if(intnum<32)
    {
        *( (volatile unsigned long *)(VIC0VECTADDR + 4*(intnum-0)) ) = (unsigned)handler;
    }
    //VIC1
    else if(intnum<64)
    {
        *( (volatile unsigned long *)(VIC1VECTADDR + 4*(intnum-32)) ) = (unsigned)handler;
    }
    //VIC2
    else if(intnum<96)
    {
        *( (volatile unsigned long *)(VIC2VECTADDR + 4*(intnum-64)) ) = (unsigned)handler;
    }
    //VIC3
    else
    {
        *( (volatile unsigned long *)(VIC3VECTADDR + 4*(intnum-96)) ) = (unsigned)handler;
    }
    return;
}


// 使能中断
// 通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号
void intc_enable(unsigned long intnum)
{
    unsigned long temp;
	// 确定intnum在哪个寄存器的哪一位
	// <32就是0~31,必然在VIC0
    if(intnum<32)
    {
        temp = VIC0INTENABLE;
        temp |= (1<<intnum);		// 如果是第一种设计则必须位操作,第二种设计可以
									// 直接写。
        VIC0INTENABLE = temp;
    }
    else if(intnum<64)
    {
        temp = VIC1INTENABLE;
        temp |= (1<<(intnum-32));
        VIC1INTENABLE = temp;
    }
    else if(intnum<96)
    {
        temp = VIC2INTENABLE;
        temp |= (1<<(intnum-64));
        VIC2INTENABLE = temp;
    }
    else if(intnum<NUM_ALL)
    {
        temp = VIC3INTENABLE;
        temp |= (1<<(intnum-96));
        VIC3INTENABLE = temp;
    }
    // NUM_ALL : enable all interrupt
    else
    {
        VIC0INTENABLE = 0xFFFFFFFF;
        VIC1INTENABLE = 0xFFFFFFFF;
        VIC2INTENABLE = 0xFFFFFFFF;
        VIC3INTENABLE = 0xFFFFFFFF;
    }

}

// 禁止中断
// 通过传参的intnum来禁止某个具体的中断源,中断号在int.h中定义,是物理中断号
void intc_disable(unsigned long intnum)
{
    unsigned long temp;

    if(intnum<32)
    {
        temp = VIC0INTENCLEAR;
        temp |= (1<<intnum);
        VIC0INTENCLEAR = temp;
    }
    else if(intnum<64)
    {
        temp = VIC1INTENCLEAR;
        temp |= (1<<(intnum-32));
        VIC1INTENCLEAR = temp;
    }
    else if(intnum<96)
    {
        temp = VIC2INTENCLEAR;
        temp |= (1<<(intnum-64));
        VIC2INTENCLEAR = temp;
    }
    else if(intnum<NUM_ALL)
    {
        temp = VIC3INTENCLEAR;
        temp |= (1<<(intnum-96));
        VIC3INTENCLEAR = temp;
    }
    // NUM_ALL : disable all interrupt
    else
    {
        VIC0INTENCLEAR = 0xFFFFFFFF;
        VIC1INTENCLEAR = 0xFFFFFFFF;
        VIC2INTENCLEAR = 0xFFFFFFFF;
        VIC3INTENCLEAR = 0xFFFFFFFF;
    }

    return;
}


// 通过读取VICnIRQSTATUS寄存器,判断其中哪个有一位为1,来得知哪个VIC发生中断了
unsigned long intc_getvicirqstatus(unsigned long ucontroller)
{
    if(ucontroller == 0)
        return	VIC0IRQSTATUS;
    else if(ucontroller == 1)
        return 	VIC1IRQSTATUS;
    else if(ucontroller == 2)
        return 	VIC2IRQSTATUS;
    else if(ucontroller == 3)
        return 	VIC3IRQSTATUS;
    else
    {}
    return 0;
}


// 真正的中断处理程序。意思就是说这里只考虑中断处理,不考虑保护/恢复现场
void irq_handler(void)
{
	//printf("irq_handler.\n");
	// SoC支持很多个(在低端CPU例如2440中有30多个,在210中有100多个)中断
	// 这么多中断irq在第一个阶段走的是一条路,都会进入到irq_handler来
	// 我们在irq_handler中要去区分究竟是哪个中断发生了,然后再去调用该中断
	// 对应的isr。
	
	
	// 虽然硬件已经自动帮我们把isr放入了VICnADDR中,但是因为有4个,所以我们必须
	// 先去软件的检查出来到底哪个VIC中断了,也就是说isr到底在哪个VICADDR寄存器中
	unsigned long vicaddr[4] = {VIC0ADDR,VIC1ADDR,VIC2ADDR,VIC3ADDR};
    int i=0;
    void (*isr)(void) = NULL;

    for(i=0; i<4; i++)
    {
		// 发生一个中断时,4个VIC中有3个是全0,1个的其中一位不是0
        if(intc_getvicirqstatus(i) != 0)
        {
            isr = (void (*)(void)) vicaddr[i];
            break;
        }
    }
    (*isr)();		// 通过函数指针来调用函数
}







key.c:
#include "stdio.h"
#include "main.h"

// 定义操作寄存器的宏
#define GPH0CON		0xE0200C00
#define GPH0DAT		0xE0200C04
#define GPH2CON		0xE0200C40
#define GPH2DAT		0xE0200C44

#define rGPH0CON	(*(volatile unsigned int *)GPH0CON)
#define rGPH0DAT	(*(volatile unsigned int *)GPH0DAT)
#define rGPH2CON	(*(volatile unsigned int *)GPH2CON)
#define rGPH2DAT	(*(volatile unsigned int *)GPH2DAT)

#define EXT_INT_0_CON	0xE0200E00
#define EXT_INT_2_CON	0xE0200E08
#define EXT_INT_0_PEND	0xE0200F40
#define EXT_INT_2_PEND	0xE0200F48
#define EXT_INT_0_MASK	0xE0200F00
#define EXT_INT_2_MASK	0xE0200F08

#define rEXT_INT_0_CON	(*(volatile unsigned int *)EXT_INT_0_CON)
#define rEXT_INT_2_CON	(*(volatile unsigned int *)EXT_INT_2_CON)
#define rEXT_INT_0_PEND	(*(volatile unsigned int *)EXT_INT_0_PEND)
#define rEXT_INT_2_PEND	(*(volatile unsigned int *)EXT_INT_2_PEND)
#define rEXT_INT_0_MASK	(*(volatile unsigned int *)EXT_INT_0_MASK)
#define rEXT_INT_2_MASK	(*(volatile unsigned int *)EXT_INT_2_MASK)



//------------------------轮询方式处理按键---------------------------
// 初始化按键
void key_init(void)
{
	// 设置GPHxCON寄存器,设置为输入模式
	// GPH0CON的bit8~15全部设置为0,即可
	rGPH0CON &= ~(0xFF<<8);
	// GPH2CON的bit0~15全部设置为0,即可
	rGPH2CON &= ~(0xFFFF<<0);
}

static void delay20ms(void)
{
	// 这个函数作用是延时20ms
	// 因为我们这里是裸机程序,且重点不是真的要消抖,而是教学
	// 所以我这里这个程序只是象征性的,并没有实体
	// 如果是研发,那就要花时间真的调试出延时20ms的程序
	int i, j;
	
	for (i=0; i<100; i++)
	{
		for (j=0; j<1000; j++)
		{
			i * j;
		}
	}
}

void key_polling(void)
{
	// 依次,挨个去读出每个GPIO的值,判断其值为1还是0.如果为1则按键按下,为0则弹起
	
	// 轮询的意思就是反复循环判断有无按键,隔很短时间
	while (1)
	{
		// 对应开发板上标着LEFT的那个按键
		if (rGPH0DAT & (1<<2))
		{
			// 为1,说明没有按键
			led_off();
		}
		else
		{
			// 添加消抖
			// 第一步,延时
			delay20ms();
			// 第二步,再次检验按键状态
			if (!(rGPH0DAT & (1<<2)))
			{
				// 为0,说明有按键
				led1();
				printf("key left.\n");
			}
		}
		
		// 对应开发板上标着DOWN的那个按键
		if (rGPH0DAT & (1<<3))
		{
			// 为1,说明没有按键
			led_off();
		}
		else
		{
			// 为0,说明有按键
			led2();
			printf("key down.\n");
		}
		
		// 对应开发板上标着UP的那个按键
		if (rGPH2DAT & (1<<0))
		{
			// 为1,说明没有按键
			led_off();
		}
		else
		{
			// 为0,说明有按键
			led3();
		}
	}
}


//-----------------------中断方式处理按键-----------------------------------
// 以中断方式来处理按键的初始化 
void key_init_interrupt(void)
{
	// 1. 外部中断对应的GPIO模式设置
	rGPH0CON |= 0xFF<<8;		// GPH0_2 GPH0_3设置为外部中断模式
	rGPH2CON |= 0xFFFF<<0;		// GPH2_0123共4个引脚设置为外部中断模式
	
	// 2. 中断触发模式设置
	rEXT_INT_0_CON &= ~(0xFF<<8);	// bit8~bit15全部清零
	rEXT_INT_0_CON |= ((2<<8)|(2<<12));		// EXT_INT2和EXT_INT3设置为下降沿触发
	rEXT_INT_2_CON &= ~(0xFFFF<<0);
	rEXT_INT_2_CON |= ((2<<0)|(2<<4)|(2<<8)|(2<<12));	
	
	// 3. 中断允许
	rEXT_INT_0_MASK &= ~(3<<2);			// 外部中断允许
	rEXT_INT_2_MASK &= ~(0x0f<<0);
	
	// 4. 清挂起,清除是写1,不是写0
	rEXT_INT_0_PEND |= (3<<2);
	rEXT_INT_2_PEND |= (0x0F<<0);
}

// EINT2通道对应的按键,就是GPH0_2引脚对应的按键,就是开发板上标了LEFT的那个按键
void isr_eint2(void)
{
	// 真正的isr应该做2件事情。
	// 第一,中断处理代码,就是真正干活的代码
	printf("isr_eint2_LEFT.\n");
	// 第二,清除中断挂起(中断处理后,必须清除中断挂起)
	rEXT_INT_0_PEND |= (1<<2);
	intc_clearvectaddr();//清除需要处理的中断的中断处理函数的地址
}

void isr_eint3(void)
{
	// 真正的isr应该做2件事情。
	// 第一,中断处理代码,就是真正干活的代码
	printf("isr_eint3_DOWN.\n");
	// 第二,清除中断挂起
	rEXT_INT_0_PEND |= (1<<3);
	intc_clearvectaddr();// 清除需要处理的中断的中断处理函数的地址
}

void isr_eint16171819(void)
{
	// 真正的isr应该做2件事情。
	// 第一,中断处理代码,就是真正干活的代码
	// 因为EINT16~31是共享中断,所以要在这里再次去区分具体是哪个子中断
	if (rEXT_INT_2_PEND & (1<<0))
	{
		printf("eint16\n");
	}
	if (rEXT_INT_2_PEND & (1<<1))
	{
		printf("eint17\n");
	}
	if (rEXT_INT_2_PEND & (1<<2))
	{
		printf("eint18\n");
	}
	if (rEXT_INT_2_PEND & (1<<3))
	{
		printf("eint19\n");
	}
  // 第二,清除中断挂起
	rEXT_INT_2_PEND |= (0x0f<<0);
	intc_clearvectaddr();
}















start.S:
#define WTCON		0xE2700000

#define SVC_STACK	0xd0037d80
#define IRQ_STACK	0xd0037f80

.global _start	
.global IRQ_handle	

// 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
	// 第1步:关看门狗(向WTCON的bit5写入0即可)
	ldr r0, =WTCON
	ldr r1, =0x0
	str r1, [r0]
	
	// 第2步:初始化时钟
	bl clock_init
	
	// 第3步:设置SVC栈
	ldr sp, =SVC_STACK
	
	// 第4步:开/关icache
	mrc p15,0,r0,c1,c0,0;			// 读出cp15的c1到r0中
	//bic r0, r0, #(1<<12)			// bit12 置0  关icache
	orr r0, r0, #(1<<12)			// bit12 置1  开icache
	mcr p15,0,r0,c1,c0,0;

	bl main
	
	// 从这里之后就可以开始调用C程序了
	//bl led_blink					// led_blink是C语言实现的一个函数
	
// 汇编最后的这个死循环不能丢
	b .
	
// 在这个汇编函数中,用来做中断模式下的现场保护和恢复,并且调用真正的中断处理程序

IRQ_handle:
	// 设置IRQ模式下的栈
	ldr sp, =IRQ_STACK
	// 保存LR
	// 因为ARM有流水线,所以PC的值会比真正执行的代码+8,
	sub lr, lr, #4
	// 保存r0-r12和lr到irq模式下的栈上面
	stmfd sp!, {r0-r12, lr}
	// 在此调用真正的isr来处理中断
	bl irq_handler
	// 处理完成开始恢复现场,其实就是做中断返回,关键是将r0-r12,pc,cpsr一起回复
	ldmfd sp!, {r0-r12, pc}^
	

main.c:
#include "stdio.h"
#include "int.h"
#include "main.h"

void uart_init(void);

#define KEY_EINT2		NUM_EINT2		// left NUM_EINT2为按键对应中断源编号
#define KEY_EINT3		NUM_EINT3		// down
#define KEY_EINT16_19	NUM_EINT16_31	// 其余4个共用的

void delay(int i)
{
	volatile int j = 10000;
	while (i--)
		while(j--);
}


int main(void)
{
	uart_init();
	
	key_init_interrupt();//中断方式处理按键的初始化
	
	// 如果程序中要使用中断,就要调用中断初始化来初步初始化中断控制器
	system_init_exception();
	
	printf("-------------key interrypt test--------------");
	
	// 绑定isr到中断控制器硬件
	intc_setvectaddr(KEY_EINT2, isr_eint2);
	intc_setvectaddr(KEY_EINT3, isr_eint3);
	intc_setvectaddr(KEY_EINT16_19, isr_eint16171819);
	
	// 使能中断
	intc_enable(KEY_EINT2);
	intc_enable(KEY_EINT3);
	intc_enable(KEY_EINT16_19);
	
	// 在这里加个心跳
	while (1)
	{
		printf("A ");
		delay(10000);
	}

	return 0;
}






作者:wangweijundeqq 发表于2017/11/25 16:13:38 原文链接
阅读:32 评论:0 查看评论

Java for Web学习笔记(九八):持久化初探(3)JPA小例子(上)

$
0
0

在tomcat中配置

我们采用数据源在tomcat中配置的方式:

  1. 数据库的配置在war之外。
    • 在很多网上资料中,这是设置在web app里面的xml中,这存在一个问题,如果配置封装在war中,当我们部署环境出现变化,最简单地,ip地址变更,账号密码变更,就需要重新封装war,当然,我们可以手动更改tomcat解析后的war包里面的配置,但对于升级和版本回退存在问题。最好的方式是保持war不变,部署环境的变化在非war包封装的配置文件中进行。
  2. 我们使用了tomcat来管理连接,这样就无需引入C3P0作为连接池管理。
    • 引入的包总是越少越好。
    • 我们之前是基于每个web app使用JDBC的,见C3P0例子,我们需要在web app卸装的时候(web app运行在container之中,不是进程),小心进行deregisterDriver,否则会引发内存泄漏的问题。将数据库连接管理池放置在Tomcat,由tomcat进行统一进行管理,Driver对所有的web app有效。
  3. 在web app中,我们无需再引入mysql-connetor的jar包,通过JPA解耦底层具体的数据库实现,便于数据库的移植。

步骤1:在tomcat中加入Mysql connector/J

从Mysql中下载Connector/J,我们使用5.1.44版本,并将其放入tomcat/lib下,这样,在下次启动tomcat时,这个lib有效。

mysql connector的版本似乎有点怪,同时维护了3个版本。貌似6似一个过渡版本,不喜欢7,8正在开发过程。如果我们选择了6版本,package的名字有所变化,Driver为com.mysql.cj.jdbc.Driver。在我的测试中,tomcat版本为8.0.36,如果使用6的版本,在web卸载是会报告:

十一月 21, 2017 11:00:21 上午 org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads警告: The web application [chapter20] appears to have started a thread named [Abandoned connection cleanup thread] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
  java.lang.Object.wait(Native Method)
  java.lang.ref.ReferenceQueue.remove(Unknown Source)
  com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:43)

关于AbandonedConnectionCleanupThread的异常是可以代码修复的,如下:

AbandonedConnectionCleanupThread.checkedShutdown(); //mysql connector 5版本在web app卸载时执行
AbandonedConnectionCleanupThread.shutdown(); //mysql connector 6版本在web app卸载时执行
但是我们已经引入的JPA作为中间层,为什么还要去调用底层mysql-connector.jar包的接口(需要在app中加入该jar)。而使用mysql-connector 5.1.44,不会出现这个异常。这也可能是因为我的tomcat版本过低问题。我们在后面的学习中,都将使用5.1.44版本。

步骤2:在tomcat中设置datasource

打开tomcat/conf/context.xml,添加下面的内容

<Resource name="jdbc/DataSourceName" type="javax.sql.DataSource"
  maxActive="20" maxIdle="5" maxWait="10000"
  username="mysqluser" password="mysqlpassword"
  driverClassName="com.mysql.jdbc.Driver"
  url="jdbcUrl" />
如果有多个数据库,则添加多个。具体例子如下,我们还需要设置SSL,autoReconnect,以及传输的字符集,&需要进行转义为&amp;,否则tomcat在启动时会报错。对于开发环境,context.xml位于我们的workspace下,如<workspace>\Servers\Tomcat v8.0 Server at localhost-config
<Resource name="jdbc/learnTest" type="javax.sql.DataSource"
	maxTotal="20" maxIdle="5" maxWaitMillis="10000"
	username="test" password="test123456"
	driverClassName="com.mysql.jdbc.Driver"
	url="jdbc:mysql://191.8.1.107:3306/test?useSSL=true&amp;autoReconnect=true&amp;useUnicode=true" />

useSSL建议打开,否则明文传递,会给出告警

Thu Nov 23 08:17:53 CST 2017 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.

在C3P0中,我们需要设置autoReconnect=true,以确保数据库连接池中连接的有效,不会因为长时间没有访问,导致连接断开失效。现在我们使用的是tomcat对连接的管理,我们测试发现:

如果长时间没有访问,在新的访问时,tomcat会重新建立一条连接,JPA进行Entity的映射,这和C3P0不同。虽然建立连接及登录过程需要一点时间,但若长时间没有访问,说明此时性能要求很低,这点时间是可以容忍的,因此autoReconnect此项设置并不重要,可以jdbc:mysql://191.8.1.107:3306/test?useSSL=true。当然,安全地,设上autoReconnect=true是保险的做法。

pom.xml

我们需要在pom.xml中增加以下内容

<!-- JPA,版本为2.2,在maven中心,没有什么官方的JPA接口,一般会使用eclipse提供的(eclipse link是JPA的范例,虽然hibernate更流行。目前提供了2.2版本,但我们仍使用2.1版本,因为Hibernate ORM的5.2.12.Final只支持2.1版本(hibernate-jpa-2.1-api)。 -->
<dependency>
    <groupId>org.eclipse.persistence</groupId>
    <artifactId>javax.persistence</artifactId>
    <version>2.1.0</version>
    <scope>compile</scope>
</dependency>	
<dependency>
    <groupId>javax.transaction</groupId>
    <artifactId>javax.transaction-api</artifactId>
    <version>1.2</version>
    <scope>compile</scope>
</dependency>
<!-- Hibernate ORM,我们不使用非JPA的私有部分,不直接调用其接口,因此用runtime即可。artifactId也可以使用hibernate-entitymanager -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.2.12.Final</version>
    <scope>runtime</scope>
    <exclusions>
        <exclusion>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.1-api</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.jboss.spec.javax.transaction</groupId>
            <artifactId>jboss-transaction-api_1.2_spec</artifactId>
            </exclusion>
        <exclusion>
            <groupId>org.jboss.logging</groupId>
            <artifactId>jboss-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

小例子的数据库

一些sql语句

在给出数据表格schema之前,我们先看看一些SQL的用法。

常用字符集和collate

utf8_unicode_ci 大小写不敏感。_ci(大小写不敏感)、_cs(大小写敏感)或_bin(binary)。
CREATE DATABASE EntityMappings DEFAULT CHARACTER SET 'utf8' DEFAULT COLLATE 'utf8_unicode_ci';

常用的字符集utf8,collate为utf8_unicode_ci,即对与名字采用大小写不敏感方式,很多name,email等会采用不敏感方式。

插入前触发

在插入表A前先触发在表B中执行某条SQL语句,下面例子表示在将数据加入表A个前,先执行:insert into B values('1','building');
delimiter $$   -- 一般使用$$,虽然分界符可以用其他,例如$,因为里面可能涉及多个SQL语句,含有缺省的分界符号;,会引发语义混淆
create trigger A_trigger before insert on A for each row 
   begin
       insert into B values('1','building');
   end;
$$
DELIMITER; -- 将分界符改回来,否要要使用show tables;$$

介绍触发,是因为小例子给出了generator,其本质就是插入前触发。

小例子的表格的schema

-- 表:作者
CREATE TABLE `Authors` (
  `AuthorId` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `AuthorName` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `EmailAddress` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`AuthorId`),
  KEY `Author_Names` (`AuthorName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 表:书
CREATE TABLE `Books` (
  `Id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `Isbn` varchar(13) COLLATE utf8_unicode_ci NOT NULL,
  `Title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `Author` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `Price` decimal(6,2) NOT NULL,
  `Publisher` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`Id`),
  UNIQUE KEY `Books_ISBNs` (`Isbn`),
  KEY `Books_Titles` (`Title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 表:出版商,和前面两个表不同,这里的Id没有使用AUTO_INCREMENT,而是通过另一个表格SurrogateKeys中的KeyValue的值获取。这是对历史项目的一个模拟,已经很少使用了。
CREATE TABLE `Publishers` (
  `PublisherId` bigint(20) unsigned NOT NULL,
  `PublisherName` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `Address` varchar(1024) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`PublisherId`),
  KEY `Publishers_Names` (`PublisherName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 表:这是个辅助Publishers表格生产PublisherId的表格。
CREATE TABLE `SurrogateKeys` (
  `TableName` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `KeyValue` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`TableName`),
  KEY `SurrogateKeys_Table_Values` (`TableName`,`KeyValue`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

相关链接: 我的Professional Java for Web Applications相关文章
作者:flowingflying 发表于2017/11/25 17:08:22 原文链接
阅读:2 评论:0 查看评论

UIAutomator2.0详解(By & BySelector & UIObject2 VS UISelector & UIObject)

$
0
0

BySelector,UISelector为UIAutomator中的查询条件类。UIDevice可以根据所指定的查询条件遍历到期望的控件对象,或者控件对象列表。

By & BySelector

其中BySelector比较特别,从源码看,其构造函数非public,无法手动创建,因此有了By工具类。By的33个静态方法都以BySelector为输出类型,覆盖BySelector所提供的36个方法。其实,只要有了BySelector对象,何愁那36个接口啊。

UIDevice可将BySelector对象作为输入参数,调用以下方法来获取UIObject2对象或UIObject2对象列表。

public UiObject2 findObject(BySelector selector) 
public List<UiObject2> findObjects(BySelector selector)

或方法

public boolean hasObject(BySelector selector) 

来判断有无符合条件的控件对象。

By接口方法与BySelector接口方法的对应关系如下图。

这里写图片描述

UISelector

那么UISelector呢?首先,UISelector可以手动创建。其次,UIDevice也提供了一个接口方法,用以寻找控件。

public UiObject findObject(UiSelector selector)

而方法的返回为UIObject类型,而非UIObject2。
先对比一下UISelector和BySelector,看一下功能上有什么区别。

这里写图片描述

通过对比可以发现,两者的功能都差不多,在接口数量上BySelector更加丰富。

当然,两者也有细微的差别。

(1)处理父子层次关系的方法不同。BySelector可以使用嵌套调用的方式。UISelector除了嵌套调用外,还提供了childSelector和fromParent两个接口方法。
(2)在使用类名进行查询的方法中,BySelector的方法全部返还BySelector对象,而UISelector可以返回class类。
(3)UISelector可以通过节点索引进行查询,index方法。
(4)UISelector提供了实例ID查询方法。instance方法。

但众多的查询方法,这些细微差别并不算什么。

UIObject Vs UIObject2

再来看看UIObject和UIObject2。两者是不同版本阶段的产物,并无任何继承关系。因此,可以简单的理解为,现有UIAutomator提供了两套遍历条件接口。

UIObject隶属于UIAutomator1.0,UIObject2则属于UIAutomator2.0。但UIAutomator2.0仍保留了UIObject对象,及其相应接口。

两者是两种不同的东西,因此没有什么对比性。若硬要对比,也只能从功能划分方面比较一下两者的接口。

这里写图片描述

这里写图片描述

以上只是一个笼统的对比。对于如何选用,还是要看测试需求。

UIAutomator1.0 VS UIAutomator2.0

这里写图片描述

图源来自https://www.cnblogs.com/yyh8/p/6902942.html

作者:daihuimaozideren 发表于2017/11/25 17:10:12 原文链接
阅读:3 评论:0 查看评论

Codeforce 839C Journey (dfs+概率)

$
0
0
C. Journey
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

There are n cities and n - 1 roads in the Seven Kingdoms, each road connects two cities and we can reach any city from any other by the roads.

Theon and Yara Greyjoy are on a horse in the first city, they are starting traveling through the roads. But the weather is foggy, so they can’t see where the horse brings them. When the horse reaches a city (including the first one), it goes to one of the cities connected to the current city. But it is a strange horse, it only goes to cities in which they weren't before. In each such city, the horse goes with equal probabilities and it stops when there are no such cities.

Let the length of each road be 1. The journey starts in the city 1. What is the expected length (expected value of length) of their journey? You can read about expected (average) value by the link https://en.wikipedia.org/wiki/Expected_value.

Input

The first line contains a single integer n (1 ≤ n ≤ 100000) — number of cities.

Then n - 1 lines follow. The i-th line of these lines contains two integers ui and vi (1 ≤ ui, vi ≤ nui ≠ vi) — the cities connected by the i-th road.

It is guaranteed that one can reach any city from any other by the roads.

Output

Print a number — the expected length of their journey. The journey starts in the city 1.

Your answer will be considered correct if its absolute or relative error does not exceed 10 - 6.

Namely: let's assume that your answer is a, and the answer of the jury is b. The checker program will consider your answer correct, if .

Examples
input
4
1 2
1 3
2 4
output
1.500000000000000
input
5
1 2
1 3
3 4
2 5
output
2.000000000000000
Note

In the first sample, their journey may end in cities 3 or 4 with equal probability. The distance to city 3 is 1 and to city 4 is 2, so the expected length is 1.5.

In the second sample, their journey may end in city 4 or 5. The distance to the both cities is 2, so the expected length is 2.

题意通俗易懂,就是求到达最后一个结点的步数的期望。

很容易想到DFS去找每一条路径,但问题是怎么处理期望问题,刚开始的想法是求出每一次的路径长度,然后求出总的路径数,最后一除做期望,但问题是这是一个1为根的树,遍历一遍整棵树就完了,根本没法这么计算,然后想用一个数组实现,数组中存到第i个结点的期望,就类似于一个记忆化搜索的题,可怎么也调试不出来,这里有一个知识点死活没想到,也可以说根本没接触过,就是当前的期望等于前一步的期望除以路径总数+1,这样dfs最后输出ans[1]就ok了,高中概率没学好吧,就差了这一步。

还有一种做法就是得到走每一条路径的概率,这样在dfs的一开始搜一遍就可以得到,用当前概率除以边的数量就等于走下一条边的概率,然后当下面没有边时,直接用概率*路径的数量就可以得到期望。dfs很简单,就是概率太难想到。

方法一代码实现:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
#include<cstdio>
#define ll long long
#define mset(a,x) memset(a,x,sizeof(a))

using namespace std;
const double PI=acos(-1);
const int inf=0x3f3f3f3f;
const double esp=1e-6;
const int maxn=1e6+5;
const int mod=1e9+7;
int dir[4][2]={0,1,1,0,0,-1,-1,0};
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}
ll fpow(ll n,ll k){ll r=1;for(;k;k>>=1){if(k&1)r=r*n%mod;n=n*n%mod;}return r;}
struct node{
	int u,v,next;
}p[maxn];
int cnt,head[maxn];
double ans[maxn];

void init()
{
	cnt=0;
	mset(head,-1);
	mset(ans,0);
}

void add(int x,int y)
{
	p[cnt].u=x;
	p[cnt].v=y;
	p[cnt].next=head[x];
	head[x]=cnt++;
	
	p[cnt].u=y;
	p[cnt].v=x;
	p[cnt].next=head[y];
	head[y]=cnt++;
}

void dfs(int x,int y)
{
    int sum=0,i;
    for(i=head[x];~i;i=p[i].next)
    {
    	if(p[i].v!=y)
	    {
	    	sum++;
	        dfs(p[i].v,x);
	        ans[x]+=ans[p[i].v];
	    }
	}
    if(sum)
    {
    	ans[x]=1.0*ans[x]/sum+1;
	}
}

int main()
{
	int n,i,j,x,y;
	while(~scanf("%d",&n))
	{
		init();
		for(i=0;i<n-1;i++)
		{
			scanf("%d%d",&x,&y);
			add(x,y);
		}
		if(n==1)
		{
			cout<<"0.000000000000000"<<endl;
			continue;
		}
		dfs(1,0);
		printf("%.8f\n",ans[1]);
	}
	return 0;
}

方法二代码实现:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
#include<cstdio>
#define ll long long
#define mset(a,x) memset(a,x,sizeof(a))

using namespace std;
const double PI=acos(-1);
const int inf=0x3f3f3f3f;
const double esp=1e-6;
const int maxn=1e6+5;
const int mod=1e9+7;
int dir[4][2]={0,1,1,0,0,-1,-1,0};
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}
ll fpow(ll n,ll k){ll r=1;for(;k;k>>=1){if(k&1)r=r*n%mod;n=n*n%mod;}return r;}
struct node{
	int u,v,next;
}p[maxn];
int cnt,head[maxn],vis[maxn];
double ans;

void init()
{
	cnt=0;
	mset(head,-1);
	mset(vis,0);
}

void add(int x,int y)
{
	p[cnt].u=x;
	p[cnt].v=y;
	p[cnt].next=head[x];
	head[x]=cnt++;
	
	p[cnt].u=y;
	p[cnt].v=x;
	p[cnt].next=head[y];
	head[y]=cnt++;
}

void dfs(int x,int f,int step,double pp)
{
   	int s=0;
   	for(int i=head[x];~i;i=p[i].next)
   	{
   		if(p[i].v!=f)
   		s++;
	}
	if(s==0)
	{
		ans+=pp*step;
		return ;
	}
	for(int i=head[x];~i;i=p[i].next)
	{
		if(!vis[p[i].v])
		{
			vis[p[i].v]=1;
			dfs(p[i].v,x,step+1,pp/s);
		}
	}
}

int main()
{
	int n,i,j,x,y;
	while(~scanf("%d",&n))
	{
		init();
		for(i=0;i<n-1;i++)
		{
			scanf("%d%d",&x,&y);
			add(x,y);
		}
		if(n==1)
		{
			cout<<"0.000000000000000"<<endl;
			continue;
		}
		vis[1]=1;
		ans=0;
		dfs(1,-1,0,1);
		printf("%.8f\n",ans);
	}
	return 0;
}


作者:Ever_glow 发表于2017/11/25 17:25:30 原文链接
阅读:0 评论:0 查看评论

jQuery学习之一---选择器

$
0
0

本博文记录了大部分常用的jq选择器,需要的小伙伴可以过来瞅一眼哦,我会陆陆续续的更新系统的jq博文,一起加油吧!

基本选择器

1.id选择器:通过id名获取

 <div id="div"></div>
 $("$div");//<div id="div"></div>

2.element:标签名选择器

<div></div>
$("div");//<div></div>

3.类名选择器:通过类名选取

<div class="div"></div>
$(".div");//<div class="div"></div>

4.*:统配选择器,匹配所有元素

<div></div>
<span></span>
$("*");//<div></div><span></span>

5.群组选择器:将每一个选择器匹配到的元素合并后一起返回

<div id="div"></div>
<span class="span"></span>
<p></p>
$("div#div,p");//<div id="div"></div>,<p></p>

层级选择器

1.后代选择器 div span

<div>
    <span>1</span>
    <div>
        <span>2</span>
    </div>
</div>
$("div span");//<span>1</span>,<span>2</span>

2.子代选择器 div>span

<div class="div">
    <span>1</span>
    <div>
        <span>2</span>
    </div>
</div>
$(".div span");//<span>1</span>
请注意观察,这个时候第二个span标签是类名为div的孙子级了哦,所以不包含它

3.兄弟相邻选择器 div + span
紧邻在div后面的span标签

<div class="div">
    <span>1</span>
    <div>1</div>
    <div>
        <span>2</span>
    </div>
</div>
$("span + div");//<div>1</div>

4.兄弟选择器 div ~ span

<div class="div">
    <span>1</span>
    <div>1</div>
    <div>2</div>
</div>
$("span ~ div");//<div>1</div><div>2</div>

基本筛选选择器

1.:first和:last
获取第一个和最后一个元素

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
$("li:first");//<li>1</li>
$("li:last");//<li>3</li>

2.:gt(index)和:lt(index)
获取比大于/小于索引值的元素

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
$("li:gt(1)");//<li>3</li>
$("li:lt(2));//<li>1</li>

3.:header,标题元素的匹配

<ul>
    <li>1</li>
    <h1></h1>
    <li>2</li>
    <h5></h5>
    <li>3</li>
</ul>
$(":header");//<h1></h1>,<h5></h5>

内容选择器

1.:has(selector),匹配含有选择器所匹配的元素的元素

<div><p>hello</p></div>
<div>hi</div>
$("div:has(p)");//<div><p>hello</p></div>

2.:parent,含有子元素或文本的元素
:empty,不含子元素或文本的元素

<div><p>hello</p></div>
<div>hi</div>
<div></div>
$("div:parent");//<div><p>hello</p></div>,<div>hi</div>
$("div:empty");//<div></div>

3.:contains(text),包含给定文本的元素

<div>hello</div>
<div>hi</div>
$("div:contains('hi')");//<div>hi</div>

可见性

:visible:匹配所有可见元素
:hidden:匹配所偶遇不可见或者type为hidden的元素

<div style="display:none"></div>
<div></div>
$("div:visible");//<div></div>
$("div:hidden");//<div style="display:none"></div>

属性选择器

1.[attr]:匹配包含给定属性的元素

<div>
  <p>Hello!</p>
</div>
<div id="test2"></div>
$("div[id]");//<div id="test2"></div>

2.[attr = value]:匹配给定的属性是某个特定值的元素
[attr != value]:匹配给定的属性不是某个特定值的元素

<div id="test1"></div>
<div id="test2"></div>
$("div[id = 'test1']");//<div id="test1"></div>
$("div[id != 'test1']");//<div id="test2"></div>

3.[attr^ = value]:匹配给定属性以value开头的元素
[attr$ = value]:匹配给定属性以value结尾的元素
[attr* = value]:匹配给定属性包含value的元素

<div id="news"></div>
<div id="youngGirl"></div>
<div id="test1"></div>
$("div[id* = '1']");//<div id="test1"></div>
$("div[id^ = 'new']");//<div id="news"></div>
$("div[id$ = 'Girl']");//<div id="youngGirl"></div>

子元素选择器

1.nth-child():匹配其父元素下的第N个子或奇偶元素

<ul>
  <li>John</li>
  <li>Karl</li>
  <li>Brandon</li>
</ul>
<ul>
  <li>Glen</li>
  <li>Tane</li>
  <li>Ralph</li>
</ul>
$("ul li:nth-child(2)");//<li>Karl</li>,<li>Tane</li>
必须是同级里面所有标签顺序的第二个

2.nth-of-type():选择同属于一个父元素之下,并且标签名相同的子元素中的第n个。

<ul>
  <li>John</li>
  <span></span>
  <li>Karl</li>
  <span></span>
  <li>Brandon</li>
</ul>
$("li:nth-of-type(2)");//<li>Karl</li>
只要是同类标签的第几个即可,不用在乎中间有没有其他的标签

3.:first-child,:last-child,:only-child
匹配所给选择器的第一个元素;最后一个元素;如果某个元素是父元素中唯一的子元素,那将会被匹配

<ul>
  <li>John</li>
  <li>Karl</li>
  <li>Brandon</li>
</ul>
<ul>
  <li>Glen</li>
  <li>Tane</li>
  <li>Ralph</li>
</ul>
<ul>
  <li>Mike</li>
</ul>
$("ul li:first-child");// <li>John</li>, <li>Glen</li>
$("ul li:last-child");// <li>Brandon</li>, <li>Ralph</li>
$("ul li:only-child");//<li>Mike</li>

4.first-of-type,last-of-type
所有所有匹配标签的第一个,最后一个,和唯一一个

<ul>
  <span></span>
  <li>Karl</li>
  <li>Brandon</li>
</ul>
<ul>
  <li>Glen</li>
  <li>Tane</li>
  <span></span>
</ul>
$("ul li:first-of-type");//  <li>Karl</li>, <li>Glen</li>
$("ul li:last-of-type");// <li>Brandon</li>, <li>Tane</li>

表单选择器

1.:input

 <input type="file" />
 <input type="hidden" />
 <input type="image" />
 $(":input");//<input type="file" />,<input type="hidden" />,<input type="image" />

2.其他的就很简单了,是哪个选择器就对应的选择标签就可以了,下面以单选框和复选框打比方。
:radio,:checkbox

<input type="checkbox" />
<input type="file" />
<input type="password" />
<input type="radio" />
$("input:radio");//<input type="radio" />
$("input:checkbox");//<input type="checkbox" />

表单对象属性

1.:enabled,:disabled
匹配所有可用的不可用的元素

<form>
  <input name="email" disabled="disabled" />
  <input name="id" />
</form>
$("input:enabled");//<input name="id" />
$("input:disabled");//<input name="email" disabled="disabled" />

2.:checked,单复选框的选中,select的option被选中

<input type="checkbox" checked="checked" />
<input type="radio" />
<select>
    <option checked="checked">1</option>
    <option></option>
</select>
$("input:checked");//<input type="checkbox" checked="checked" />
$("option:checked");//<option checked="checked">1</option>

3.:selected:匹配所有被选中的option元素

<select>
    <option checked="checked">1</option>
    <option>2</option>
    <option checked="checked">3</option>
</select>
$("select option:selected");//<option checked="checked">1</option>,<option checked="checked">3</option>
作者:MYTLJP 发表于2017/11/25 17:38:23 原文链接
阅读:0 评论:0 查看评论

MyBatis动态sql_where查询条件

$
0
0

MyBatis动态sql_where查询条件



EmployeeMapperDynamicSQL.java

package com.cn.mybatis.dao;

import java.util.List;

import com.cn.zhu.bean.Employee;

public interface  EmployeeMapperDynamicSQL {
	//<!-- 查询员工,要求,携带了哪个字段查询条件就带上这个字段的值 -->
	public List<Employee> getEmpsByConditionIf(Employee  employee);
	
}

<?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.cn.mybatis.dao.EmployeeMapperDynamicSQL">
	<!-- 查询员工,要求,携带了哪个字段查询条件就带上这个字段的值 -->
	<!--public List<Employee> getEmpsByConditionIf(Employee  employee);  -->
	<select id="getEmpsByConditionIf" resultType="com.cn.zhu.bean.Employee">
		select * from tbl_employee  where 
			<!--
				test 判断表达式(OGNL) C:if test OGNL参照ppt或者官方文档 c:if test 从参数中取值进行判断
				遇见特殊符号应该去写转义字符
			-->
			<if test="id!=null">
				 id=#{id}
        </if>
			<if test="lastName!=null && lastName!=""">
				and last_name like #{lastName} 
        </if>
			<if test="email !=null  and email.trim()!=""">
				and email=#{email} 
        </if>
			<!-- ognl 会进行字符串和数字的转换   "0"==0 -->
			<if test="gender==0  or  gender==1">
				and gender=#{gender}
        </if>
	</select>
</mapper>
测试程序

@Test
	public void  testDynamicSql() throws  IOException{
		SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
		SqlSession openSession=sqlSessionFactory.openSession();
		try {
			EmployeeMapperDynamicSQL   mapper=	openSession.getMapper(EmployeeMapperDynamicSQL.class);	
			Employee employee=new Employee(3, "%h%", "zhu@qq.com", null);
			List<Employee>  emps=  mapper.getEmpsByConditionIf(employee);
			for(Employee emp: emps)
				System.out.println(emp);
			
		} catch (Exception e) {
			// TODO: handle exceptio
			e.printStackTrace();
		}

	}
测试结果



这样就能正常查询数据,如果当主键为空的时候,就会报错,那么怎么解决呢?


会多出一个  and

第一种方法: 在where 后面加上  where  1=1

<?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.cn.mybatis.dao.EmployeeMapperDynamicSQL">
	<!-- 查询员工,要求,携带了哪个字段查询条件就带上这个字段的值 -->
	<!--public List<Employee> getEmpsByConditionIf(Employee  employee);  -->
	<select id="getEmpsByConditionIf" resultType="com.cn.zhu.bean.Employee">
		select * from tbl_employee	
		where 1=1
			<!--
				test 判断表达式(OGNL) C:if test OGNL参照ppt或者官方文档 c:if test 从参数中取值进行判断
				遇见特殊符号应该去写转义字符
			-->
			<if test="id!=null">
				 id=#{id}
        </if>
			<if test="lastName!=null && lastName!=""">
				and last_name like #{lastName} 
        </if>
			<if test="email !=null  and email.trim()!=""">
				and email=#{email} 
        </if>
			<!-- ognl 会进行字符串和数字的转换   "0"==0 -->
			<if test="gender==0  or  gender==1">
				and gender=#{gender}
        </if>
	</select>
</mapper>

第二种解决方式: 用where标签  <where >  </where>

<?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.cn.mybatis.dao.EmployeeMapperDynamicSQL">
	<!-- 查询员工,要求,携带了哪个字段查询条件就带上这个字段的值 -->
	<!--public List<Employee> getEmpsByConditionIf(Employee  employee);  -->
	<select id="getEmpsByConditionIf" resultType="com.cn.zhu.bean.Employee">
		select * from tbl_employee
		<where>
			<!--
				test 判断表达式(OGNL) C:if test OGNL参照ppt或者官方文档 c:if test 从参数中取值进行判断
				遇见特殊符号应该去写转义字符
			-->
			<if test="id!=null">
				 id=#{id}
        </if>
			<if test="lastName!=null && lastName!=""">
				and last_name like #{lastName} 
        </if>
			<if test="email !=null  and email.trim()!=""">
				and email=#{email} 
        </if>
			<!-- ognl 会进行字符串和数字的转换   "0"==0 -->
			<if test="gender==0  or  gender==1">
				and gender=#{gender}
        </if>
		</where>
	</select>
</mapper>

测试程序

@Test
	public void  testDynamicSql() throws  IOException{
		SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
		SqlSession openSession=sqlSessionFactory.openSession();
		try {
			EmployeeMapperDynamicSQL   mapper=	openSession.getMapper(EmployeeMapperDynamicSQL.class);	
			Employee employee=new Employee(null, "%h%", null, null);
			List<Employee>  emps=  mapper.getEmpsByConditionIf(employee);
			for(Employee emp: emps)
				System.out.println(emp);
			// 查询的时候如果某些条件没带可能sql拼装会有问题
			// 1.给where 后面加上1=1,以后的条件都and  
			//2.  mybatis 使用where 标签将所有的查询条件包括在内。
			// mybatis 就会将where标签后面第一个and 去掉
			// where只会去掉第一个多出来的and 或者  or
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

	}


测试结果





                    // 查询的时候如果某些条件没带可能sql拼装会有问题
// 1.给where 后面加上1=1,以后的条件都and  
//2.  mybatis 使用where 标签将所有的查询条件包括在内。
// mybatis 就会将where标签后面第一个and 去掉
// where只会去掉第一个多出来的and 或者  or


更多请关注微信公众号:


作者:zhupengqq 发表于2017/11/25 16:24:17 原文链接
阅读:18 评论:0 查看评论

MyBatis动态sql_trim自定义字符串截取

$
0
0

MyBatis动态sql_trim自定义字符串截取



继续上次文章http://blog.csdn.net/zhupengqq/article/details/78632444

EmployeeMapperDynamicSQL.java

package com.cn.mybatis.dao;

import java.util.List;

import com.cn.zhu.bean.Employee;

public interface  EmployeeMapperDynamicSQL {
	//<!-- 查询员工,要求,携带了哪个字段查询条件就带上这个字段的值 -->
	public List<Employee> getEmpsByConditionIf(Employee  employee);
	//测试trim截取字符串
	public List<Employee> getEmpsByConditionTrim(Employee  employee);
}

EmployeeMapperDynamicSQL.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.cn.mybatis.dao.EmployeeMapperDynamicSQL">
	
	<!-- public List<Employee> getEmpsByConditionTrim(Employee  employee); -->
	<select id="getEmpsByConditionTrim" resultType="com.cn.zhu.bean.Employee">
	  select *  from tbl_employee
	  <!-- 后面多出的and或者or  where标签不能解决 
	  prefix=""  前缀: trim标签体中是整个字符串拼串后的结果
	          prefix给拼串后的整个字符加一个前缀  
	   prefixOverrides="" 
	          前缀覆盖: 去掉整个字符串前面多余的字符
	   suffixOverrides="" 
	   后缀覆盖  去掉整个字符串前面多余的字符
	   suffix=""	后缀  
	   给拼串后的整个字符加一个后缀  	  
	  -->
	  
	  <!-- 自定义字符串截取规则 -->
	  <trim prefix="where"  suffixOverrides="and">
	  	<if test="id!=null">
				 id=#{id}  and
        </if>
			<if test="lastName!=null && lastName!=""">
			last_name like #{lastName}  and
        </if>
			<if test="email !=null  and email.trim()!=""">
				email=#{email} and
        </if>
			<!-- ognl 会进行字符串和数字的转换   "0"==0 -->
			<if test="gender==0  or  gender==1">
				 gender=#{gender} 
        </if>
	 </trim>  
	</select>
	
</mapper>
MyBatisTest.java

@Test
	public void  testDynamicSql() throws  IOException{
		SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
		SqlSession openSession=sqlSessionFactory.openSession();
		try {
			EmployeeMapperDynamicSQL   mapper=	openSession.getMapper(EmployeeMapperDynamicSQL.class);	
			//测试if   where 
			Employee employee=new Employee(null, "%h%", null, null);
			List<Employee>  emps=  mapper.getEmpsByConditionIf(employee);
			for(Employee emp: emps)
				System.out.println(emp);
			// 查询的时候如果某些条件没带可能sql拼装会有问题
			// 1.给where 后面加上1=1,以后的条件都and  
			//2.  mybatis 使用where 标签将所有的查询条件包括在内。
			// mybatis 就会将where标签后面第一个and 去掉
			// where只会去掉第一个多出来的and 或者  or
			List<Employee>  emps2=mapper.getEmpsByConditionTrim(employee);
			for(Employee emp: emps2 ){
				System.out.println(emp);
			}
			//测试Trim
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

	}



更多文章请关注微信公众号:




作者:zhupengqq 发表于2017/11/25 17:17:50 原文链接
阅读:36 评论:0 查看评论

Java for Web学习笔记(九九):持久化初探(4)JPA小例子(下)

$
0
0

Entity的映射

虽然在前面,我们给出了表格。但书推荐我们先设计代码,然后根据代码来设计数据库,用户需求-》代码设计-》数据库设计。但我觉得大项目可能不会这样。

类和数据的映射

//表明这是 @javax.persistence.Entity,缺省Entity的名字是类名,如果需要特指,可通过用name参数。
@Entity(name = "PublisherEntity")
/*可以不设置,缺省的表名字就是entity的名字,本例如果不设定,则为PublisherEntity,现表名为Publishers。如果我们允许创建表格(注意,这是危险的,在生产环境中应当禁止)。我们可以在此设置主键外的其他key,例如:
 * @Table(name = "Books",  //表名
 *        uniqueConstraints = { @UniqueConstraint(name = "Books_ISBNs", columnNames = { "isbn" })}, // unique key
 *        indexes = { @Index(name = "Books_Titles", columnList = "title")}) // key */
@Table(name = "Publishers",
       indexes = {@Index(name = "Publishers_Names", columnList = "PublisherName")) 
//可以不设置,缺省为AccessType.PROPERTY,表示表的列名字是根据getter和setter方法,即Java Bean property;另一个值是就AccessType.FIELD,即根据field的名字。一般应使用缺省值。
@Access(AccessType.FIELD)
public class Publisher implements Serializable{ .....}

主键映射:单一主键

@Entity(name = "PublisherEntity")
@Table(name = "Publishers", indexes = {@Index(name = "Publishers_Names", columnList = "PublisherName"))})
public class Publisher implements Serializable{
    private long id;  //这就是field
    private String name;
    private String address;

    /* 对于标记annotation,我们要么都加载field上,要么都加载property上,不要两者混用,避免出现混淆
     *【1】我们首先要设定surrogate key,也就是SQL中的primary key,可能是单列,也可能是联合主键。*/
    // 1.1】设置主键。主键是唯一的,如果代码没有设置@Id,如果有getId和setId,则视为该方法有@Id。
    @Id 
    //【2】设置自动生成的值
    // 2.1】如果是MySQL主键的AUTO_INCREMENT(Microsoft SQL Server和Sysbase中称为IDENTITY),为@GeneratedValue(strategy = GenerationType.IDENTITY)
    /* 2.2】通过生成器的方式来设置主键的生成。生成器是全局有效的,在一处设置后,其他也可以使用,生成器的定义可以在class上,但是@GeneratedValue必须在property或者field上。有GenerationType.SEQUENCE和GenerationType.TABLE两种,对应@SequenceGenerator和@TableGenerator。
    ➤ @SequenceGenerator,如何产生主键的值(在Oracle中称为sequence,也就是流水号的生产方式),属性有initialValue(初始值缺省0),allocationSize(步进值,缺省50)。
    ➤ @TableGenerator,使用一个专门的表格来存放当前的流水号,如例子所示:使用了表SurrogateKeys,主键为TableName,值为Publishers,另一列为KeyValue,初始值11923,步进为1;也就是说在当前表增加一个entry时,表SurrogateKeys中TableName=Publishers的KeyValue加一,并将这个值作为本entity的id(列为PublisherId)的值。需要注意的是table,pkColumnName,pkColumnValue,valueColumnName的缺省值并没有在JPA中规定,因此不同的实现会有不同,为了避免歧义,建议明确定义。然而,我们很少使用这种方式,可能用于历史遗留项目。GenerationType.IDENTITY或GenerationType.SEQUENCE能满足我们的要求*/
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "PublisherGenerator")
    @TableGenerator(name = "PublisherGenerator", table = "SurrogateKeys",
                    pkColumnName = "TableName", pkColumnValue = "Publishers",
                    valueColumnName = "KeyValue", initialValue = 11923, allocationSize = 1)
    //【3】@Column用于对列的设置
    // @Column的参数 name:指定列名;insertable和updatable是权限
    // @Column的schema相关参数
    /*   -  nullable:NULL或者NOT NULL;
     *   -  unique相当于@UniqueConstraint,缺省为false;
     *   -  length用于VARBINARY和VARCHAR的长度,缺省为255;
     *   -  scale和precision用于Decimal;
     *   -  columnDefinition指定生成列的SQL,但是由于不同SQL Server在语句上会有区别,一旦使用,就很难迁移到其他数据库,因此不建议 */
    @Column(name = "PublisherId")
    public final long getId() {
        return id;
    }
}

主键映射:复合主键

方式1:使用@IdClass

表格SomeJoinTable有联合组件(fooParentTableSk,barParentTableSk),代码如下:

public class JoinTableCompositeId implements Serializable{
    private long fooParentTableSk;
    private long barParentTableSk;

    public long getFooParentTableSk() { ... }
    public void setFooParentTableSk(long fooParentTableSk) { ... }
    public long getBarParentTableSk() { ... }
    public void setBarParentTableSk(long barParentTableSk) { ... }
}

@Entity
@Table(name = "SomeJoinTable")
@IdClass(JoinTableCompositeId.class)
public class JoinTableEntity implements Serializable{
    private long fooParentTableSk;
    private long barParentTableSk;
    ...

    @Id
    public long getFooParentTableSk() { ... }
    public void setFooParentTableSk(long fooParentTableSk) { ... }

    @Id
    public long getBarParentTableSk() { ... }
    public void setBarParentTableSk(long barParentTableSk) { ... }
    ...
}

方式2:使用@EmbeddedId

我们看到方式1中,在getter和setter的代码有重复,可以采用下面的方式:

@Embeddable
public class JoinTableCompositeId implements Serializable{
    private long fooParentTableSk;
    private long barParentTableSk;

    public long getFooParentTableSk() { ... }
    public void setFooParentTableSk(long fooParentTableSk) { ... }
    public long getBarParentTableSk() { ... }
    public void setBarParentTableSk(long barParentTableSk) { ... }
}

@Entity
@Table(name = "SomeJoinTable")
public class JoinTableEntity implements Serializable{
    private JoinTableCompositeId id;

    @EmbeddedId
    public JoinTableCompositeId getId() { ... }
    public void setId(JoinTableCompositeId id) { ... }
}

数据类型映射

JPA中property的数据类型 数据库中column的数据类型
short,Short SMALLINT, INTEGER, BIGINT或相应的类型
int,Integer INTEGER, BIGINT或相应的类型
long,Long BIGINT或相应的类型
float, Float, double, Double, BigDecimal DECIMAL 或相应的类型
byte,Byte BINARY, SMALLINT, INTEGER, BIGINT或相应的类型
char,Char CHAR, VARCHAR, BINARY, SMALLINT, INTEGER, BIGINT或相应的类型
boolean,Boolean BOOLEAN, BIT, SMALLINT, INTEGER, BIGINT, CHAR, VARCHAR或相应的类型
byte[],Byte[] BINARY, VARBINARY或相应的类型
char[], Character[],String CHAR, VARCHAR, BINARY,VARBINARY或相应的类型
java.util.Date, Calendar DATE, DATETIME, TIME或相应的类型,需要加上@Temporal
java.sql.Timestamp DATETIME
java.sql.Date DATE
java.sql.Time Time
Enum SMALLINT, INTEGER, BIGINT, CHAR, VARCHAR或相应的类型,可以通过@Enumerated来变更存储方式,在后面介绍。
Serializable VARBINARY或相应的类型,用于Java的序列化和反序列化

/*使用@Basic可以进行上表中的类型匹配,optional表示是否可以为null,本例表示NOT NULL。对于原型,如int,double等是没有null这一说的,因此在原型时,不设置option说明,数据库中必定是NOT NULL */
@Basic(optional=false)
public final String getIsbn() {
   return isbn;
}

设置Persistence

Persistent Unit Scope是配置和Entity类,其中的EntityManager实例只能方位该持续化单元,不能越界。Entity类可以属于多个持续化单元。持续化单元通过persistence.xml配置,必须放置在META-INF/下。一个web项目有多个META-INF:

mappings.war!/META-INF  这不在classes/目录下,不能被class文件访问,用来放置Servlet容器所需的文件,所以不能放这里
mappings.war!/WEB-INF/classes/META-INF  放在这里,在eclipse中,即在resources/下串接META-INF/
mappings.war!/WEB-INF/lib/something.jar!/META-INF  这是其他jar包所带的META-INF目录,我们也放不进去,而且其作用只在该jar包内


下面是例子的persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
                                http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
            version="2.1">
    <!-- 有一个或者多个persistence-unit -->
    <!-- transaction-type:在J2EE应用服务器中缺省为JTA(Java Transaction API),在J2SE和简单的servlet容器中缺省为标准本地事务 RESOURCE_LOCAL。为避免歧义,我们应明确设置 -->
    <!-- persistence-unit里面可以为空,但是如果设置,必须要顺序 -->
    <persistence-unit name="EntityMappingsTest" transaction-type="RESOURCE_LOCAL">
        <!-- 首先是<description>,小例子不提供 -->
        <!-- provider:具体的javax.persistence.spi.PersistenceProvider实现,缺省值为classpath中第一个JPA的实现 -->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <!-- 如果persistence-unit的transaction-type为JTA,使用<jta-data-source>,如果为RESOURCE_LOCAL,使用<non-jta-data-source>,采用JNDI(Java Naming and Directory Interface)方式,给出数据源。 -->
        <non-jta-data-source>java:comp/env/jdbc/learnTest</non-jta-data-source>
        <!-- <mapping-file>:基于classpath的XML mapping file,如果不指定,缺省为orm.xml,可以设置多个mapping-file -->
        <!-- <jar-file>:JPA实现将对jar包进行绑定标记扫描,如果里面有@Entity,@Embeddable,@javax.persistence.MappedSuperclass或者@javax.persistence.Converter,加入本持续化单元中,可以设置多个jar-file -->
        <!-- <class>:JPA实现将对这个class加入到持续化单元中,这个class必须带有@Entity,@Embeddable,@javax.persistence.MappedSuperclass或者@javax.persistence.Converter。可以设置多个class -->
        <!-- 设置<exclude-unlisted-classes/>或者<exclude-unlisted-classes>true</exclude-unlisted-classes>表示只关注在jar-file和在class中所设置的,不扫描其他。删除<exclude-unlisted-classes/>或者<exclude-unlisted-classes>false</exclude-unlisted-classes>则表示将扫描classpath位置;如果本文件在JAR文件,则扫描JAR文件的classes,如果本文件位于classes中的某个特定目录,则只扫描该目录下的文件(例如指定到某个package)。 -->
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <!-- <shared-cache-mode>:是否缓存entity,
        ➤ NONE表示不缓存,
        ➤ ALL表示缓存所有的entities。
        ➤ ENABLE_SELECTIVE 表示只缓存带有@Cacheable或者@Cacheable(true)标识的entity 
        ➤ DISABLE_SELECTIVE 表示除了@Cacheable(false)外均缓存
        ➤ UNSPECIFIED 表示有JPA的提供者来决定,Hibernate ORM缺省为ENABLE_SELECTIVE,但采用这种方式,对于移植可能会存在混淆,应明确设定 -->
        <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
        <!-- <validation-mode>
        ➤ NONE表示不使用Bean validator,
        ➤ CALLBACK表示在写操作(insert,update,delete)前进行validate
        ➤ AUTO,如果classpath中存在Bean validator provider,则CALLBACK,不存在则NONE
        如果使用validate,而我们自定义了spring framework的validator,JPA将忽略这个自定义。因此建议使用NONE,在数据持久化之前进行校验,而不是在持久化这个层面。 -->
        <validation-mode>NONE</validation-mode>
        <!-- <properties>以name-value的方式提供其他JPA属性(如JDBC连接,用户,密码,schema产生成设置等)以及提供者特有的属性(如Hibernate设置)。 -->
        <properties>
            <!-- 禁止schema生成,即不根据entity创建表格 -->
            <property name="javax.persistence.schema-generation.database.action" value="none" />
        </properties>
    </persistence-unit>
</persistence>

持续化的代码

我们以servlet为例,演示如何查询表格和insert表格。
@WebServlet(
    name ="EntityServlet",
    urlPatterns = "/entities",
    loadOnStartup = 1)
public class EntityServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private final Random random;
    //Entity管理器:在初始化时获取,在结束是关闭
    private EntityManagerFactory factory;
       
    public EntityServlet() {
        super();
        try {
            this.random = SecureRandom.getInstanceStrong();
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException(e);
        }
    }

    /** 【1.1】在初始化时创建EntityManagerFactory。持续化单元的名字见persistence.xml。
      * 对于完全J2EE server(Tomcat不是),不需要这样,采用:
      *   @PersistenceContext("EntityMappingsTest")
      *   EntityManagerFactory factory; */
    @Override
    public void init() throws ServletException {
        super.init();
        this.factory = Persistence.createEntityManagerFactory("EntityMappingsTest");
    }

    /**【1.2】在结束时关闭EntityManagerFactory */
    @Override
    public void destroy() {
        this.factory.close();	
        super.destroy();
    }	

    /** 【2】我们在doGet中演示获取表格内容,其中,演示了对transaction处理的常规代码 */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        EntityManager manager = null;
        EntityTransaction transaction = null;
        try{
            manager = this.factory.createEntityManager();
            transaction = manager.getTransaction();
            transaction.begin();

            //读表Publisher(简单的采用全获取的方式)             
            CriteriaBuilder builder = manager.getCriteriaBuilder();//返回用于CriteriaQuery objects的CriteriaBuilder实例
            CriteriaQuery<Publisher> q1 = builder.createQuery(Publisher.class);			
            Root<Publisher> q1Root = q1.from(Publisher.class);
            TypedQuery<Publisher> queryResult = manager.createQuery(q1.select(q1Root));
            request.setAttribute("publishers", queryResult.getResultList());

            //读表Author(简单的采用全获取的方式)   			
            CriteriaQuery<Author> q2 = builder.createQuery(Author.class);
            request.setAttribute("authors", manager.createQuery(q2.select(q2.from(Author.class))).getResultList());

            CriteriaQuery<Book> q3 = builder.createQuery(Book.class);
            request.setAttribute("books", manager.createQuery(q3.select(q3.from(Book.class))).getResultList());

            transaction.commit();
            request.getRequestDispatcher("/WEB-INF/jsp/view/entities.jsp").forward(request, response);			
        }catch(Exception e){
            if(transaction != null && transaction.isActive())
                transaction.rollback();
            e.printStackTrace(response.getWriter()); //简单地在页面输出,正式项目不会如此处理
        }finally{
            if(manager != null && manager.isOpen())
                manager.close();
        }
    }

    /** 【3】我们在doPost中演示insert */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        EntityManager manager = null;
        EntityTransaction transaction = null;
        try{
            manager = this.factory.createEntityManager();
            transaction = manager.getTransaction();
            transaction.begin();

            Publisher publisher = new Publisher();
            publisher.setName("John Wiley & Sons");
            publisher.setAddress("1234 Baker Street");
            manager.persist(publisher);

            Author author = new Author();
            author.setName("Nicholas S. Williams");
            author.setEmailAddress("nick@example.com");
            manager.persist(author);

            Book book = new Book();
            book.setIsbn("" + this.random.nextInt(Integer.MAX_VALUE));
            book.setTitle("Professional Java for Web Applications");
            book.setAuthor("Nicholas S. Williams");
            book.setPublisher("John Wiley & Sons");
            book.setPrice(59.99D);
            manager.persist(book);

            transaction.commit();
            response.sendRedirect(request.getContextPath() + "/entities");
        }catch(Exception e){
            if(transaction != null && transaction.isActive())
                transaction.rollback();
            e.printStackTrace(response.getWriter());
        }finally{
            if(manager != null && manager.isOpen())
                manager.close();
        }
    }
}

相关链接: 我的Professional Java for Web Applications相关文章

作者:flowingflying 发表于2017/11/25 17:36:09 原文链接
阅读:52 评论:0 查看评论

(一)Android官方MVVM框架实现组件化之整体结构

$
0
0

目前的项目结构图置顶:Demo的Github地址: https://github.com/Dawish/GoogleArchitectureDemo

0-演示项目MVVM组件化架构图

一、google官方MVVM框架讲解

我前面对比了MVC和MVP《两张图看懂Android开发中MVC与MVP的区别》,可以相对于MVC我们的MVP是有多优越,但是Android开发现在已经开始流行了MVVM,前不久google官方发布了MVVM的正式库。官方的正式MVVM库主要包括下面四个:
1-正式MVVM库组件

其中只有ViewModel是MVVM结构中的一个组件,其他的三个都是辅助性质的。

lifecycles 就是处理UI界面的生命周期,在26版本以后的Support库中,AppCompatActivitySupportActivity中都实现了LifecycleOwner,内部已经对UI界面的生命周期做了处理了。

LiveData是一个抽象类,我们可以存放UI页面需要的数据,就是把数据包装在LiveData中了,我们可以观测LiveData中的数据变化,但是LiveData是跟UI的生命周期关联的,当UI页面销毁了,LiveData的数据变化回调是不会执行的。

Room 就是一个sqlite数据持久化库,我们也可以使用别的ORM库。

二、MVVM架构优势

《两张图看懂Android开发中MVC与MVP的区别》 前面两张图真是了MVC和MVP的区别,我这里也来一张图看看MVVM:

2-MVVM架构

看上图ModelView是不会发生关系的,ViewModel是把View和Model关联起来的加工厂:

3-ViewModel工厂

MVVM优势总结:

  1. ViewModel双向绑定,一方的改变都会影响另一方,开发者不用再去手动修改UI的数据。额,互相自动的。

  2. 不需要findViewById也不需要butterknife,不需要拿到具体的View去设置数据绑定监听器等等,这些都可以用DataBinding完成。是不是很舒服?

  3. ViewModel的双向绑定是支持生命周期检测的,不会担心页面销毁了还有回调发生,这个由lifeCycle完成。

  4. 不会像MVC一样导致Activity中代码量巨大,也不会像MVP一样出现大量的ViewPresenter接口。项目结构更加低耦合。

  5. 更低的耦合把各个模块分开开发,分开测试,可以分给不同的开发人员来完成。

三、MVVM组件化示例项目架构分析

下图是项目模块和工程之间的依赖关系:
4-MVVM组件化示例项目架构图
下图是工程Android Studio中的目录结构:
5-工程目录结构

3.1 各模块和彼此之间的关系解释:

  • lib_opensource :第三方build.gradle依赖,本项目主要有supportlifecycleroomfrescoretrofitokhttpRxJavaARouter这些。

  • lib_coremodel: 存放MVVM中的ModelViewModel两个模块,就是数据的处理和数据与UI页面的绑定。依赖lib_opensource库。

  • lib_common : 公共库,主要有各种base,各种ui组件,自定义组件,公用的Activity、公用的Fragment,和公用的utils等等。依赖lib_coremodel库。

  • module_girls : 妹子功能模块,可以在libraryapplication之间切换,自己可以是一个app也可以成为别的app的一个组件模块。组件化编译时为app,反之为module。

  • module_news : 新闻功能模块,可以在libraryapplication之间切换,自己可以是一个app也可以成为别的app的一个组件模块。组件化编译时为app,反之为module。

  • app_universal : 定制版本的app,组件化编译时 module_girlsmodule_news为app,所以不能把这两个作为module加进来编译,所以组件化编译时app_universal要依赖lib_common库,反之就可以把 module_girlsmodule_news作为module加进来编译。

  • app_specific : 定制版本的app,组件化编译时 module_girlsmodule_news为app,所以不能把这两个作为module加进来编译,所以组件化编译时app_specific要依赖lib_common库,反之就可以把 module_girlsmodule_news作为module加进来编译。

3.2 ARouter串联各个模块

使用ARouter来跳转Activity和获取Fragment,记得看之前别人的组件化结构文章,一直都在纠结Fragment的获取问题,我想说的是有了ARouter来获取Fragment不是超级简单么?

ARouter典型应用

  • 从外部URL映射到内部页面,以及参数传递与解析
  • 跨模块页面跳转,模块间解耦
  • 拦截跳转过程,处理登陆、埋点等逻辑
  • 跨模块API调用,通过控制反转来做组件解耦

3.3 组件化编译和非组件化编译切换

我们在工程根目录下的gradle.properties文件中加入一个Boolean类型的变量,通过修改这个变量来识别编译模式:

# 每次更改“isModule”的值后,需要点击 "Sync Project" 按钮
# isModule是“集成开发模式”和“组件开发模式”的切换开关
isModule=false

然后在 module_girlsmodule_news中的build.gradle文件中支持切换:

if (isModule.toBoolean()) {
    //组件化编译时为application
    apply plugin: 'com.android.application'
} else {
    //非组件化编译时为library
    apply plugin: 'com.android.library'
}

android {
    compileSdkVersion build_versions.target_sdk
    buildToolsVersion build_versions.build_tools

    defaultConfig {
        minSdkVersion build_versions.min_sdk
        targetSdkVersion build_versions.target_sdk
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        //ARouter
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [moduleName: project.getName()]
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    dataBinding {
        enabled = true
    }
    lintOptions {
        abortOnError false
    }
    sourceSets {
        main {
            if (isModule.toBoolean()) {
                //组件化编译时为app,在对应的AndroidManifest文件中需要写ndroid.intent.action.MAIN入口Activity
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                //集成开发模式下排除debug文件夹中的所有Java文件
                java {
                    //debug文件夹中放的是Application类,非组件化时不用有此类
                    exclude 'debug/**'
                }
            }
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    api project(':lib_coremodel')
    api project(':lib_common')
    implementation 'com.android.support:support-v4:26.1.0'
    annotationProcessor deps.arouter.compiler
}

上面看到了组件化和非组件化编译会有不用的AndroidManifest文件,组件化时需要debug文件夹下面的application类,非组件化时排除此文件夹。
6-组件化非组件化编译切换

  • module下的AndroidManifest文件是组件化app编译时的,写了MAIN入口Activity
  • dubug下是组件化app编译时的Application类,初始化作为一个app运行时需要的资源等等。在非组件化编译在build.gradle文件中排除debug文件夹的所以东西。

3.4 最后预告:

后面会有一些列介绍在MVVM组件化过程中使用ARouter来跳转Activity和获取FragmentDataBinding实现数据和UI的互相绑定、Rxjava2Retrofit2动态数据获取,和AndroidViewModel的封装。

下面贴贴一个lib_coremodel库中我封装的AndroidViewModel,用泛型来确定数据类型,并且是动态URL获取数据:

package google.architecture.coremodel.viewmodel;

import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.databinding.ObservableField;
import android.support.annotation.NonNull;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;

import google.architecture.coremodel.datamodel.http.ApiClient;
import google.architecture.coremodel.datamodel.http.ApiConstants;
import google.architecture.coremodel.datamodel.http.service.DynamicApiService;
import google.architecture.coremodel.util.JsonUtil;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.ResponseBody;

/**
 * Created by dxx on 2017/11/20.
 */

public class BaseViewModel<T> extends AndroidViewModel {

    //生命周期观察的数据
    private MutableLiveData<T>  liveObservableData = new MutableLiveData<>();
    //UI使用可观察的数据 ObservableField是一个包装类
    public ObservableField<T> uiObservableData = new ObservableField<>();

    private final CompositeDisposable mDisposable = new CompositeDisposable();

    private static final MutableLiveData ABSENT = new MutableLiveData();
    {
        //noinspection unchecked
        ABSENT.setValue(null);
    }


    public BaseViewModel(@NonNull Application application, String fullUrl) {
        super(application);
        ApiClient.initService(ApiConstants.GankHost, DynamicApiService.class).getDynamicData(fullUrl).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<ResponseBody>() {
            @Override
            public void onSubscribe(Disposable d) {
                mDisposable.add(d);
            }

            @Override
            public void onNext(ResponseBody value) {
               if(null != value){
                   try {
                       liveObservableData.setValue(JsonUtil.Str2JsonBean(value.string(), getTClass()));
                   } catch (IOException e) {
                       e.printStackTrace();
                   }
               }
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        });
    }

    /**
     * LiveData支持了lifecycle生命周期检测
     * @return
     */
    public LiveData<T> getLiveObservableData() {
        return liveObservableData;
    }

    /**
     * 当主动改变数据时重新设置被观察的数据
     * @param product
     */
    public void setUiObservableData(T product) {
        this.uiObservableData.set(product);
    }

    public Class<T> getTClass(){
        Class<T> tClass = (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        return tClass;
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        mDisposable.clear();
    }
}

Demo的Github地址: https://github.com/Dawish/GoogleArchitectureDemo

作者:u010072711 发表于2017/11/25 17:40:12 原文链接
阅读:88 评论:0 查看评论

Java for Web学习笔记(一百):持久化初探(5)Enum、时间和lob的类型匹配

$
0
0

ENUM

缺省的,ENUM对应为0,1,2,3,但这种方式可读性较差,且如果代码的顺序发生变化,将出现严重错误。更好地采用字符串方式,在数据库中可以是VARCHAR,也可以用ENUM。我们对小例子的Author表进行改造

CREATE TABLE `Authors` (
  `AuthorId` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `AuthorName` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `EmailAddress` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `Gender` enum('MALE','FEMAIL','UNSPECIFIED') COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`AuthorId`),
  KEY `Publishers_Names` (`AuthorName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
在代码中创建一个Gender的Enum

public enum Gender {
	MALE,
	FEMALE,
	UNSPECIFIED
}
在Entity中添加Gender属性,不采用@Basic的类型适配,而是给出@Enumerated,并指定类型为字符串EnumType.STRING。
@Entity
@Table(name = "Authors",
	indexes = { @Index(name = "Authors_Names", columnList = "AuthorName")})
public class Author implements Serializable{
    private Gender gender;

    //......
    @Enumerated(EnumType.STRING)
    public Gender getGender() {
        return gender;
    }
    public void setGender(Gender gender) {
        this.gender = gender;
    }
}

时间转换

JPA2.1的时间不支持Java SE 8的新的时间日期类,要JPA2.2才支持。SQL中一般为date,time,datetime(timestamp),存储的时区为SQL Server的时区,即本身不带时区信息。下面提供一个将date转为Calendar的例子。我们将定所有的服务器都在同一个时区,即不考虑时区差异。在Publishers表格中添加一个时间列。具体如下:
CREATE TABLE `Publishers` (
  `PublisherId` bigint(20) unsigned NOT NULL,
  `PublisherName` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `Address` varchar(1024) COLLATE utf8_unicode_ci NOT NULL,
  `DateFounded` date NOT NULL DEFAULT '2000-01-01',
  PRIMARY KEY (`PublisherId`),
  KEY `Publishers_Names` (`PublisherName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
在Entity中使用@Temporal来指明数据库中的格式,date,time,datetime(timestamp)分别对应为TemporalType.DATE,TemporalType.TIME,TemporalType.TIMESTAMP。因为JPA2.1还不能支持LocalDateTime的数据格式转换,因此如果我们需要转为这个格式,需要另外写。例子中给出了一个public LocalDateTime getFounded()作为格式转换,但是founded不对应到数据库表格的列,要注明@javax.persistence.Transient。
@Entity(name="PublisherEntity")
@Table(name = "Publishers",
	indexes = {@Index(name = "Publishers_Names", columnList = "PublisherName")})
public class Publisher implements Serializable{
    //... ...
    private Calendar dateFounded;
    
    //使用@Temporal来指明数据库中的格式
    @Temporal(TemporalType.DATE)
    public Calendar getDateFounded() {
        return dateFounded;
    }
    public void setDateFounded(Calendar dateFounded) {
        this.dateFounded = dateFounded;
    }
 
    //getFounded只作为数据格式的转换,并不作为数据库表格的列映射项,因此加上@Transient
    @Transient
    public LocalDateTime getFounded(){
        return LocalDateTime.ofInstant(this.dateFounded.toInstant(), this.dateFounded.getTimeZone().toZoneId());
    }
}

LOB(Large Object)

在mysql的表格中,每一行最大的容量为65535,如果存储的内容很大,例如直接存储一个图片,需要采用TEXT和BLOB格式,TEXT和BLOB的具体内容不直接存放在表格中,在表格中只占有9~12字节。虽然可以,但我们并不会使用TEXT和BLOB的数据作为index,也不会进行排序(默认的只选择签名1024字节进行排序)。我们根据存储容量[1]需要,选择具体的格式:
  • TINYBLOB,TINYTEXT: 255 (2^8- 1) bytes/charaters
  • BLOB,TEXT: 65,535 (2^16 - 1) bytes/charaters
  • MEDIUMBLOB,MEDUIMTEXT: 16,777,215 or 16M (2^24 - 1) bytes/charaters
  • LONGBLOB,LONGTEXT: 4,294,967,295 or 4GB (2^32 - 1) bytes/charaters

A BLOB is a binary large object that can hold a variable amount of data. The four BLOB types are TINYBLOB, BLOB, MEDIUMBLOB, and LONGBLOB. These differ only in the maximum length of the values they can hold. The four TEXT types are TINYTEXT, TEXT, MEDIUMTEXT, and LONGTEXT. These correspond to the four BLOB types and have the same maximum lengths and storage requirements.[2]
The internal representation of a table has a maximum row size of 65,535 bytes, even if the storage engine is capable of supporting larger rows. This figure excludes BLOB or TEXT columns, which contribute only 9 to 12 bytes toward this size. For BLOB and TEXT data, the information is stored internally in a different area of memory than the row buffer. Different storage engines handle the allocation and storage of this data in different ways, according to the method they use for handling the corresponding types.[3]

我们在小例子的Books表中加上一个列,直接binary存放预告pdf。
CREATE TABLE `Books` (
  `Id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `Isbn` varchar(13) COLLATE utf8_unicode_ci NOT NULL,
  `Title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `Author` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `Price` decimal(6,2) NOT NULL,
  `Publisher` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `PreviewPdf` mediumblob,
  PRIMARY KEY (`Id`),
  UNIQUE KEY `Books_ISBNs` (`Isbn`),
  KEY `Books_Titles` (`Title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- SQL查看二进制的方式为hex()
SELECT Id, Isbn, Title, Author, Price, Publisher, hex(PreviewPdf) FROM Books;
相应地在Entity中增加下面的代码,类型映射中指明为@Lob。@Lob可用于byte[]以及Serializable,String和char[]。
@Entity
@Table(name="Books",
	uniqueConstraints = {
        @UniqueConstraint(name = "Books_ISBNs", columnNames = { "Isbn" })},
	indexes = {
	        @Index(name = "Books_Titles", columnList = "Title")
	})
public class Book {
	// ... ...
	private byte[] previewPdf;   
	//... ...

	@Lob
	public byte[] getPreviewPdf() {
		return previewPdf;
	}
	public void setPreviewPdf(byte[] previewPdf) {
		this.previewPdf = previewPdf;
	} 
}
相关链接:我的Professional Java for Web Applications相关文章

作者:flowingflying 发表于2017/11/25 17:42:58 原文链接
阅读:68 评论:0 查看评论

Qt--日志

$
0
0

Qt提供了qDebug、qWarning、qCritical、qFatal、qInfo等不同类别的日志记录函数

qlogging.h中声明了一个日志处理函数

typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);

通过qInstallMessageHandler可以安装自己的日志处理器

QtMessageHandler qInstallMessageHandler(QtMessageHandler);

默认的日志处理器是将msg输出到stderr标准错误,如果我们想记录到日志文件中,可以参考下面代码示例:

#include <QDateTime>
void myLogHandler(QtMsgType type, const QMessageLogContext & ctx, const QString & msg){
    char szType[16];
    switch (type) {
    case QtDebugMsg:
        strcpy(szType, "Debug");
        break;
    case QtInfoMsg:
        strcpy(szType, "Info");
        break;
    case QtWarningMsg:
        strcpy(szType, "Warning");
        break;
    case QtCriticalMsg:
        strcpy(szType, "Critical");
        break;
    case QtFatalMsg:
        strcpy(szType, "Fatal");
    }

    QString strLog = QString::asprintf("[%s] [%s]:%s [%s:%d - %s]\n",
                                       QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz").toLocal8Bit().data(),
                                       szType,
                                       msg.toUtf8().data(),
                                       ctx.file,ctx.line,ctx.function);

    QString strLogFilePath = QCoreApplication::applicationDirPath() + "/" +
            QCoreApplication::applicationName() + ".log";

    FILE* fp = fopen(strLogFilePath.toLocal8Bit().data(), "a");
    if (fp){
        fseek(fp, 0, SEEK_END);
        if (ftell(fp) > (2 << 20)){
            fclose(fp);
            fp = fopen(strLogFilePath.toLocal8Bit().data(), "w");
        }
    }

    if (fp){
        fputs(strLog.toLocal8Bit().data(), fp);
        fclose(fp);
    }
}

#include <QApplication>
#include <QLabel>
#include <QDebug>
int main(int argc,char* argv[]){
    QApplication app(argc, argv);

    qInstallMessageHandler(myLogHandler);

    qDebug("app start!");

    QLabel label("开启Qt学习之旅");
    QFont font = label.font();
    font.setPixelSize(48);
    label.setFont(font);
    label.setAlignment(Qt::AlignCenter);
    label.setStyleSheet("background-color: black; color: green;");
    label.setGeometry(200,200,400,300);
    label.show();

    qDebug() << label.text();
    qDebug() << label.geometry();

    app.exec();

    qDebug("app end!");
}

appname.log文件中打印如下:

[2017-11-25 17:59:54.245] [Debug]:app start! [..\core01\main.cpp:66 - int main(int, char**)]
[2017-11-25 17:59:54.298] [Debug]:"开启Qt学习之旅" [..\core01\main.cpp:77 - int main(int, char**)]
[2017-11-25 17:59:54.298] [Debug]:QRect(200,200 400x300) [..\core01\main.cpp:78 - int main(int, char**)]
[2017-11-25 17:59:55.788] [Debug]:app end! [..\core01\main.cpp:82 - int main(int, char**)]
作者:GG_SiMiDa 发表于2017/11/25 17:58:54 原文链接
阅读:63 评论:0 查看评论

MySQL mysqldump数据导出详解

$
0
0

转自https://www.cnblogs.com/chenmh/p/5300370.html

介绍

 在日常维护工作当中经常会需要对数据进行导出操作,而mysqldump是导出数据过程中使用非常频繁的一个工具;它自带的功能参数非常多,文章中会列举出一些常用的操作,在文章末尾会将所有的参数详细说明列出来。

 

语法:

默认不带参数的导出,导出文本内容大概如下:创建数据库判断语句-删除表-创建表-锁表-禁用索引-插入数据-启用索引-解锁表

Usage: mysqldump [OPTIONS] database [tables]
OR     mysqldump [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3...]
OR     mysqldump [OPTIONS] --all-databases [OPTIONS]

插入测试数据

复制代码
CREATE DATABASE db1 DEFAULT CHARSET utf8;
USE db1;
CREATE TABLE a1(id int);
insert into a1() values(1),(2);
CREATE TABLE a2(id int);
insert into a2() values(2);
CREATE TABLE a3(id int);
insert into a3() values(3);


CREATE DATABASE db2 DEFAULT CHARSET utf8;
USE db2;
CREATE TABLE b1(id int);
insert into b1() values(1);
CREATE TABLE b2(id int);
insert into b2() values(2);
复制代码

 

1.导出所有数据库

该命令会导出包括系统数据库在内的所有数据库

mysqldump -uroot -proot --all-databases >/tmp/all.sql

2.导出db1、db2两个数据库的所有数据

mysqldump -uroot -proot --databases db1 db2 >/tmp/user.sql

3.导出db1中的a1、a2表

注意导出指定表只能针对一个数据库进行导出,且导出的内容中和导出数据库也不一样,导出指定表的导出文本中没有创建数据库的判断语句,只有删除表-创建表-导入数据

mysqldump -uroot -proot --databases db1 --tables a1 a2  >/tmp/db1.sql

4.条件导出,导出db1表a1中id=1的数据

如果多个表的条件相同可以一次性导出多个表

字段是整形

mysqldump -uroot -proot --databases db1 --tables a1 --where='id=1'  >/tmp/a1.sql

字段是字符串,并且导出的sql中不包含drop table,create table

mysqldump -uroot -proot --no-create-info --databases db1 --tables a1 --where="id='a'"  >/tmp/a1.sql

5.生成新的binlog文件,-F

有时候会希望导出数据之后生成一个新的binlog文件,只需要加上-F参数即可

mysqldump -uroot -proot --databases db1 -F >/tmp/db1.sql

6.只导出表结构不导出数据,--no-data

mysqldump -uroot -proot --no-data --databases db1 >/tmp/db1.sql

7.跨服务器导出导入数据

mysqldump --host=h1 -uroot -proot --databases db1 |mysql --host=h2 -uroot -proot db2

将h1服务器中的db1数据库的所有数据导入到h2中的db2数据库中,db2的数据库必须存在否则会报错

mysqldump --host=192.168.80.137 -uroot -proot -C --databases test |mysql --host=192.168.80.133 -uroot -proot test 

 加上-C参数可以启用压缩传递。

8.将主库的binlog位置和文件名追加到导出数据的文件中,--dump-slave

注意:--dump-slave命令如果当前服务器是从服务器那么使用该命令会执行stop slave来获取master binlog的文件和位置,等备份完后会自动执行start slave启动从服务器。但是如果是大的数据量备份会给从和主的延时变的更大,使用--dump-slave获取到的只是当前的从服务器的数据执行到的主的binglog的位置是(relay_mater_log_file,exec_master_log_pos),而不是主服务器当前的binlog执行的位置,主要是取决于主从的数据延时。

该参数在在从服务器上执行,相当于执行show slave status。当设置为1时,将会以CHANGE MASTER命令输出到数据文件;设置为2时,会在change前加上注释。

该选项将会打开--lock-all-tables,除非--single-transaction被指定。

在执行完后会自动关闭--lock-tables选项。--dump-slave默认是1

mysqldump -uroot -proot --dump-slave=1 --databases db1 >/tmp/db1.sql

mysqldump -uroot -proot --dump-slave=2 --database db1 >/tmp/db1.sql

9.将当前服务器的binlog的位置和文件名追加到输出文件,--master-data

该参数和--dump-slave方法一样,只是它是记录的是当前服务器的binlog,相当于执行show master status,状态(file,position)的值。

注意:--master-data不会停止当前服务器的主从服务

10.--opt

等同于--add-drop-table, --add-locks, --create-options, --quick, --extended-insert, --lock-tables, --set-charset, --disable-keys 该选项默认开启, 可以用--skip-opt禁用.

mysqldump -uroot -p --host=localhost --all-databases --opt

11.保证导出的一致性状态--single-transaction

该选项在导出数据之前提交一个BEGIN SQL语句,BEGIN 不会阻塞任何应用程序且能保证导出时数据库的一致性状态。它只适用于多版本存储引擎(它不显示加锁通过判断版本来对比数据),仅InnoDB。本选项和--lock-tables 选项是互斥的,因为LOCK TABLES 会使任何挂起的事务隐含提交。要想导出大表的话,应结合使用--quick 选项。

--quick, -q
不缓冲查询,直接导出到标准输出。默认为打开状态,使用--skip-quick取消该选项。

12.--lock-tables, -l

开始导出前,锁定所有表。用READ LOCAL锁定表以允许MyISAM表并行插入。对于支持事务的表例如InnoDB和BDB,--single-transaction是一个更好的选择,因为它根本不需要锁定表。

请注意当导出多个数据库时,--lock-tables分别为每个数据库锁定表。因此,该选项不能保证导出文件中的表在数据库之间的逻辑一致性。不同数据库表的导出状态可以完全不同。

13.导出存储过程和自定义函数--routines, -R

mysqldump  -uroot -p --host=localhost --all-databases --routines

14.压缩备份 

压缩备份
mysqldump -uroot -proot --databases abc 2>/dev/null |gzip >/abc.sql.gz
还原
gunzip -c abc.sql.gz |mysql -uroot -proot abc

 

参数说明:

复制代码
--all-databases  , -A
导出全部数据库。
mysqldump  -uroot -p --all-databases
--all-tablespaces  , -Y
导出全部表空间。
mysqldump  -uroot -p --all-databases --all-tablespaces
--no-tablespaces  , -y
不导出任何表空间信息。
mysqldump  -uroot -p --all-databases --no-tablespaces
--add-drop-database
每个数据库创建之前添加drop数据库语句。
mysqldump  -uroot -p --all-databases --add-drop-database
--add-drop-table
每个数据表创建之前添加drop数据表语句。(默认为打开状态,使用--skip-add-drop-table取消选项)
mysqldump  -uroot -p --all-databases  (默认添加drop语句)
mysqldump  -uroot -p --all-databases –skip-add-drop-table  (取消drop语句)
--add-locks
在每个表导出之前增加LOCK TABLES并且之后UNLOCK  TABLE。(默认为打开状态,使用--skip-add-locks取消选项)
mysqldump  -uroot -p --all-databases  (默认添加LOCK语句)
mysqldump  -uroot -p --all-databases –skip-add-locks   (取消LOCK语句)
--allow-keywords
允许创建是关键词的列名字。这由表名前缀于每个列名做到。
mysqldump  -uroot -p --all-databases --allow-keywords
--apply-slave-statements'CHANGE MASTER'前添加'STOP SLAVE',并且在导出的最后添加'START SLAVE'。
mysqldump  -uroot -p --all-databases --apply-slave-statements
--character-sets-dir
字符集文件的目录
mysqldump  -uroot -p --all-databases  --character-sets-dir=/usr/local/mysql/share/mysql/charsets
--comments
附加注释信息。默认为打开,可以用--skip-comments取消
mysqldump  -uroot -p --all-databases  (默认记录注释)
mysqldump  -uroot -p --all-databases --skip-comments   (取消注释)
--compatible
导出的数据将和其它数据库或旧版本的MySQL 相兼容。值可以为ansi、mysql323、mysql40、postgresql、oracle、mssql、db2、maxdb、no_key_options、no_tables_options、no_field_options等,
要使用几个值,用逗号将它们隔开。它并不保证能完全兼容,而是尽量兼容。
mysqldump  -uroot -p --all-databases --compatible=ansi
--compact
导出更少的输出信息(用于调试)。去掉注释和头尾等结构。可以使用选项:--skip-add-drop-table  --skip-add-locks --skip-comments --skip-disable-keys
mysqldump  -uroot -p --all-databases --compact
--complete-insert,  -c
使用完整的insert语句(包含列名称)。这么做能提高插入效率,但是可能会受到max_allowed_packet参数的影响而导致插入失败。
mysqldump  -uroot -p --all-databases --complete-insert
--compress, -C
在客户端和服务器之间启用压缩传递所有信息
mysqldump  -uroot -p --all-databases --compress
--create-options,  -a
在CREATE TABLE语句中包括所有MySQL特性选项。(默认为打开状态)
mysqldump  -uroot -p --all-databases
--databases,  -B
导出几个数据库。参数后面所有名字参量都被看作数据库名。
mysqldump  -uroot -p --databases test mysql
--debug
输出debug信息,用于调试。默认值为:d:t,/tmp/mysqldump.trace
mysqldump  -uroot -p --all-databases --debug
mysqldump  -uroot -p --all-databases --debug=” d:t,/tmp/debug.trace”
--debug-check
检查内存和打开文件使用说明并退出。
mysqldump  -uroot -p --all-databases --debug-check
--debug-info
输出调试信息并退出
mysqldump  -uroot -p --all-databases --debug-info
--default-character-set
设置默认字符集,默认值为utf8
mysqldump  -uroot -p --all-databases --default-character-set=utf8
--delayed-insert
采用延时插入方式(INSERT DELAYED)导出数据
mysqldump  -uroot -p --all-databases --delayed-insert
--delete-master-logs
master备份后删除日志. 这个参数将自动激活--master-data。
mysqldump  -uroot -p --all-databases --delete-master-logs
--disable-keys
对于每个表,用/*!40000 ALTER TABLE tbl_name DISABLE KEYS */;和/*!40000 ALTER TABLE tbl_name ENABLE KEYS */;语句引用INSERT语句。这样可以更快地导入dump出来的文件,因为它是在插入所有行后创建索引的。该选项只适合MyISAM表,默认为打开状态。
mysqldump  -uroot -p --all-databases 
--dump-slave
该选项将主的binlog位置和文件名追加到导出数据的文件中(show slave status)。设置为1时,将会以CHANGE MASTER命令输出到数据文件;设置为2时,会在change前加上注释。该选项将会打开--lock-all-tables,除非--single-transaction被指定。该选项会自动关闭--lock-tables选项。默认值为0。
mysqldump  -uroot -p --all-databases --dump-slave=1
mysqldump  -uroot -p --all-databases --dump-slave=2
--master-data
该选项将当前服务器的binlog的位置和文件名追加到输出文件中(show master status)。如果为1,将会输出CHANGE MASTER 命令;如果为2,输出的CHANGE  MASTER命令前添加注释信息。该选项将打开--lock-all-tables 选项,除非--single-transaction也被指定(在这种情况下,全局读锁在开始导出时获得很短的时间;其他内容参考下面的--single-transaction选项)。该选项自动关闭--lock-tables选项。
mysqldump  -uroot -p --host=localhost --all-databases --master-data=1;
mysqldump  -uroot -p --host=localhost --all-databases --master-data=2;
--events, -E
导出事件。
mysqldump  -uroot -p --all-databases --events
--extended-insert,  -e
使用具有多个VALUES列的INSERT语法。这样使导出文件更小,并加速导入时的速度。默认为打开状态,使用--skip-extended-insert取消选项。
mysqldump  -uroot -p --all-databases
mysqldump  -uroot -p --all-databases--skip-extended-insert   (取消选项)
--fields-terminated-by
导出文件中忽略给定字段。与--tab选项一起使用,不能用于--databases和--all-databases选项
mysqldump  -uroot -p test test --tab=”/home/mysql” --fields-terminated-by=”#”
--fields-enclosed-by
输出文件中的各个字段用给定字符包裹。与--tab选项一起使用,不能用于--databases和--all-databases选项
mysqldump  -uroot -p test test --tab=”/home/mysql” --fields-enclosed-by=”#”
--fields-optionally-enclosed-by
输出文件中的各个字段用给定字符选择性包裹。与--tab选项一起使用,不能用于--databases和--all-databases选项
mysqldump  -uroot -p test test --tab=”/home/mysql”  --fields-enclosed-by=”#” --fields-optionally-enclosed-by  =”#”
--fields-escaped-by
输出文件中的各个字段忽略给定字符。与--tab选项一起使用,不能用于--databases和--all-databases选项
mysqldump  -uroot -p mysql user --tab=”/home/mysql” --fields-escaped-by=”#”
--flush-logs
开始导出之前刷新日志。
请注意:假如一次导出多个数据库(使用选项--databases或者--all-databases),将会逐个数据库刷新日志。除使用--lock-all-tables或者--master-data外。在这种情况下,日志将会被刷新一次,相应的所以表同时被锁定。因此,如果打算同时导出和刷新日志应该使用--lock-all-tables 或者--master-data 和--flush-logs。
mysqldump  -uroot -p --all-databases --flush-logs
--flush-privileges
在导出mysql数据库之后,发出一条FLUSH  PRIVILEGES 语句。为了正确恢复,该选项应该用于导出mysql数据库和依赖mysql数据库数据的任何时候。
mysqldump  -uroot -p --all-databases --flush-privileges
--force
在导出过程中忽略出现的SQL错误。
mysqldump  -uroot -p --all-databases --force
--help
显示帮助信息并退出。
mysqldump  --help
--hex-blob
使用十六进制格式导出二进制字符串字段。如果有二进制数据就必须使用该选项。影响到的字段类型有BINARY、VARBINARY、BLOB。
mysqldump  -uroot -p --all-databases --hex-blob
--host, -h
需要导出的主机信息
mysqldump  -uroot -p --host=localhost --all-databases
--ignore-table
不导出指定表。指定忽略多个表时,需要重复多次,每次一个表。每个表必须同时指定数据库和表名。例如:--ignore-table=database.table1 --ignore-table=database.table2 ……
mysqldump  -uroot -p --host=localhost --all-databases --ignore-table=mysql.user
--include-master-host-port--dump-slave产生的'CHANGE  MASTER TO..'语句中增加'MASTER_HOST=<host>,MASTER_PORT=<port>'  
mysqldump  -uroot -p --host=localhost --all-databases --include-master-host-port
--insert-ignore
在插入行时使用INSERT IGNORE语句.
mysqldump  -uroot -p --host=localhost --all-databases --insert-ignore
--lines-terminated-by
输出文件的每行用给定字符串划分。与--tab选项一起使用,不能用于--databases和--all-databases选项。
mysqldump  -uroot -p --host=localhost test test --tab=”/tmp/mysql”  --lines-terminated-by=”##”
--lock-all-tables,  -x
提交请求锁定所有数据库中的所有表,以保证数据的一致性。这是一个全局读锁,并且自动关闭--single-transaction 和--lock-tables 选项。
mysqldump  -uroot -p --host=localhost --all-databases --lock-all-tables
--lock-tables,  -l
开始导出前,锁定所有表。用READ  LOCAL锁定表以允许MyISAM表并行插入。对于支持事务的表例如InnoDB和BDB,--single-transaction是一个更好的选择,因为它根本不需要锁定表。
请注意当导出多个数据库时,--lock-tables分别为每个数据库锁定表。因此,该选项不能保证导出文件中的表在数据库之间的逻辑一致性。不同数据库表的导出状态可以完全不同。
mysqldump  -uroot -p --host=localhost --all-databases --lock-tables
--log-error
附加警告和错误信息到给定文件
mysqldump  -uroot -p --host=localhost --all-databases  --log-error=/tmp/mysqldump_error_log.err
--max_allowed_packet
服务器发送和接受的最大包长度。
mysqldump  -uroot -p --host=localhost --all-databases --max_allowed_packet=10240
--net_buffer_length
TCP/IP和socket连接的缓存大小。
mysqldump  -uroot -p --host=localhost --all-databases --net_buffer_length=1024
--no-autocommit
使用autocommit/commit 语句包裹表。
mysqldump  -uroot -p --host=localhost --all-databases --no-autocommit
--no-create-db,  -n
只导出数据,而不添加CREATE DATABASE 语句。
mysqldump  -uroot -p --host=localhost --all-databases --no-create-db
--no-create-info,  -t
只导出数据,而不添加CREATE TABLE 语句。
mysqldump  -uroot -p --host=localhost --all-databases --no-create-info
--no-data, -d
不导出任何数据,只导出数据库表结构。
mysqldump  -uroot -p --host=localhost --all-databases --no-data
--no-set-names,  -N
等同于--skip-set-charset
mysqldump  -uroot -p --host=localhost --all-databases --no-set-names
--opt
等同于--add-drop-table,  --add-locks, --create-options, --quick, --extended-insert, --lock-tables,  --set-charset, --disable-keys 该选项默认开启,  可以用--skip-opt禁用.
mysqldump  -uroot -p --host=localhost --all-databases --opt
--order-by-primary
如果存在主键,或者第一个唯一键,对每个表的记录进行排序。在导出MyISAM表到InnoDB表时有效,但会使得导出工作花费很长时间。 
mysqldump  -uroot -p --host=localhost --all-databases --order-by-primary
--password, -p
连接数据库密码
--pipe(windows系统可用)
使用命名管道连接mysql
mysqldump  -uroot -p --host=localhost --all-databases --pipe
--port, -P
连接数据库端口号
--protocol
使用的连接协议,包括:tcp, socket, pipe, memory.
mysqldump  -uroot -p --host=localhost --all-databases --protocol=tcp
--quick, -q
不缓冲查询,直接导出到标准输出。默认为打开状态,使用--skip-quick取消该选项。
mysqldump  -uroot -p --host=localhost --all-databases 
mysqldump  -uroot -p --host=localhost --all-databases --skip-quick
--quote-names,-Q
使用(`)引起表和列名。默认为打开状态,使用--skip-quote-names取消该选项。
mysqldump  -uroot -p --host=localhost --all-databases
mysqldump  -uroot -p --host=localhost --all-databases --skip-quote-names
--replace
使用REPLACE INTO 取代INSERT INTO.
mysqldump  -uroot -p --host=localhost --all-databases --replace
--result-file,  -r
直接输出到指定文件中。该选项应该用在使用回车换行对(\\r\\n)换行的系统上(例如:DOS,Windows)。该选项确保只有一行被使用。
mysqldump  -uroot -p --host=localhost --all-databases --result-file=/tmp/mysqldump_result_file.txt
--routines, -R
导出存储过程以及自定义函数。
mysqldump  -uroot -p --host=localhost --all-databases --routines
--set-charset
添加'SET NAMES  default_character_set'到输出文件。默认为打开状态,使用--skip-set-charset关闭选项。
mysqldump  -uroot -p --host=localhost --all-databases 
mysqldump  -uroot -p --host=localhost --all-databases --skip-set-charset
--single-transaction
该选项在导出数据之前提交一个BEGIN SQL语句,BEGIN 不会阻塞任何应用程序且能保证导出时数据库的一致性状态。它只适用于多版本存储引擎,仅InnoDB。本选项和--lock-tables 选项是互斥的,因为LOCK  TABLES 会使任何挂起的事务隐含提交。要想导出大表的话,应结合使用--quick 选项。
mysqldump  -uroot -p --host=localhost --all-databases --single-transaction
--dump-date
将导出时间添加到输出文件中。默认为打开状态,使用--skip-dump-date关闭选项。
mysqldump  -uroot -p --host=localhost --all-databases
mysqldump  -uroot -p --host=localhost --all-databases --skip-dump-date
--skip-opt
禁用–opt选项.
mysqldump  -uroot -p --host=localhost --all-databases --skip-opt
--socket,-S
指定连接mysql的socket文件位置,默认路径/tmp/mysql.sock
mysqldump  -uroot -p --host=localhost --all-databases --socket=/tmp/mysqld.sock
--tab,-T
为每个表在给定路径创建tab分割的文本文件。注意:仅仅用于mysqldump和mysqld服务器运行在相同机器上。注意使用--tab不能指定--databases参数
mysqldump  -uroot -p --host=localhost test test --tab="/home/mysql"
--tables
覆盖--databases (-B)参数,指定需要导出的表名,在后面的版本会使用table取代tables。
mysqldump  -uroot -p --host=localhost --databases test --tables test
--triggers
导出触发器。该选项默认启用,用--skip-triggers禁用它。
mysqldump  -uroot -p --host=localhost --all-databases --triggers
--tz-utc
在导出顶部设置时区TIME_ZONE='+00:00' ,以保证在不同时区导出的TIMESTAMP 数据或者数据被移动其他时区时的正确性。
mysqldump  -uroot -p --host=localhost --all-databases --tz-utc
--user, -u
指定连接的用户名。
--verbose, --v
输出多种平台信息。
--version, -V
输出mysqldump版本信息并退出
--where, -w
只转储给定的WHERE条件选择的记录。请注意如果条件包含命令解释符专用空格或字符,一定要将条件引用起来。
mysqldump  -uroot -p --host=localhost --all-databases --where=” user=’root’”
--xml, -X
导出XML格式.
mysqldump  -uroot -p --host=localhost --all-databases --xml
--plugin_dir
客户端插件的目录,用于兼容不同的插件版本。
mysqldump  -uroot -p --host=localhost --all-databases --plugin_dir=”/usr/local/lib/plugin”
--default_auth
客户端插件默认使用权限。
mysqldump  -uroot -p --host=localhost --all-databases --default-auth=”/usr/local/lib/plugin/<PLUGIN>”
复制代码

错误处理 

1.unknown option '--no-beep' 

第一种方法:删除my.ini[client]下的 no-beep 参数;
第二种方法:在 mysqldump 后加--no-defaults参数 。

 

总结

文章中列举了一些常用的导出操作,还有很多其它的参数也会经常用到,包括“--add-drop-database”,“--apply-slave-statements”,“--triggers”等。客户端的导入导出功能也是不错的选择,比如workbench、navicat;其中navicat的导出向导中可以有很多文件格式可以选择。

--tab的快速导出导入数据是个不错的方法,它会在指定的目录下生成一个sql表结构文件和一个text数据文件


作者:yanzongshuai 发表于2017/11/25 18:24:19 原文链接
阅读:37 评论:0 查看评论

MyBatis动态sql_choose分支选择

$
0
0

MyBatis动态sql_choose分支选择

EmployeeMapperDynamicSQL.java

package com.cn.mybatis.dao;

import java.util.List;

import com.cn.zhu.bean.Employee;

public interface  EmployeeMapperDynamicSQL {
	//<!-- 查询员工,要求,携带了哪个字段查询条件就带上这个字段的值 -->
	public List<Employee> getEmpsByConditionIf(Employee  employee);
	//测试trim截取字符串
	public List<Employee> getEmpsByConditionTrim(Employee  employee);
	//  测试choose
	public List<Employee> getEmpsByConditionChose(Employee  employee);
}

EmployeeMapperDynamicSQL.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.cn.mybatis.dao.EmployeeMapperDynamicSQL">
  <!-- 
     if: 判断
     choose (when otherwise): 分支选择: Switch-case;
              如果 带了id就用id查,如果带了lastName就用lastName查
     trim 字符串截取(where set)
     foreach
   -->

	<!-- public List<Employee> getEmpsByConditionChose(Employee  employee); -->
	<select id="getEmpsByConditionChose" resultType="com.cn.zhu.bean.Employee">
	    select *  from tbl_employee 
       <where>
          <!-- 如果id就用id,如果带了lastName就用lastName查
                          只会进入一个
           -->
           <choose >
              <when test="id!=null">
                 id=#{id}
              </when>
               <when test="lastName!=null">
               last_name like #{lastName}
              </when>
               <when test="email!=null">
               email = #{email}
              </when>
              <otherwise>
                  gender=0
              </otherwise>
           </choose>
       </where>
	</select>
</mapper>
测试类

	@Test
	public void  testDynamicSql() throws  IOException{
		SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
		SqlSession openSession=sqlSessionFactory.openSession();
		try {
			EmployeeMapperDynamicSQL   mapper=	openSession.getMapper(EmployeeMapperDynamicSQL.class);	
			//测试if   where 
			Employee employee=new Employee(null, null, null, null);
			
			//测试choose
			List<Employee> emps3 = mapper.getEmpsByConditionChose(employee);
			for (Employee emp : emps3) {
				System.out.println(emp);
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

	}
测试结果



更多文章请关注微信公众号



作者:zhupengqq 发表于2017/11/25 18:47:40 原文链接
阅读:39 评论:0 查看评论

常见多线程面试题之Thread的join()方法

$
0
0

join简介

join方法是Thread类中的一个方法,该方法的定义是等待该线程终止。其实就是join()方法将挂起调用线程的执行,直到被调用的对象完成它的执行。这段话难理解,后面我会用实例做讲解。

join实例

现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?这个问题是网上很热门的面试题目(这里除了用join之外还有很多其他方法能够实现,只是使用join是最简单的方案),下面是实现的代码:

/**
 * 现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?
 * @author RJH
 * 2017年11月24日
 */
public class JoinDemo {

    public static void main(String[] args) {
        //初始化线程t1,由于后续有匿名内部类调用这个对象,需要用final修饰
        final Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                System.out.println("t1 is running");
            }
        });
        //初始化线程t2,由于后续有匿名内部类调用这个对象,需要用final修饰
        final Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    //t1调用join方法,t2会等待t1运行完之后才会开始执行后续代码
                    t1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("t2 is running");
                }
            }
        });
        //初始化线程t3
        Thread t3 = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    //t2调用join方法,t3会等待t2运行完之后才会开始执行后续代码
                    t2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("t3 is running");
                }
            }
        });
        //依次启动3个线程
        t1.start();
        t2.start();
        t3.start();
    }
}

输出结果:

t1 is running
t2 is running
t3 is running

结果分析:
在t2线程中t2本身就是调用线程,所谓的调用线程是指调用了t.join()方法的线程,而被调用的对象指的是调用join方法的线程对象,即t1。所以这3个线程按照t1->t2->t3的顺序执行了。

join方法源码分析

/**
 *等待该线程终止的时间最长为 millis 毫秒。超时为 0 意味着要一直等下去。
 *millis - 以毫秒为单位的等待时间。
 */
public final synchronized void join(long millis) 
throws InterruptedException {
    //获取启动时的时间戳,用于计算当前时间
    long base = System.currentTimeMillis();
    //当前时间
    long now = 0;

    if (millis < 0) {//等待时间不能小于0则抛出IllegalArgumentException
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {//等待时间为0,则无限等待
        //需要注意,如果当前线程未被启动或者终止,则isAlive方法返回false
        //即意味着join方法不会生效
        while (isAlive()) {
            wait(0);
        }
    } else {
        //需要注意,如果当前线程未被启动或者终止,则isAlive方法返回false
        //即意味着join方法不会生效
        while (isAlive()) {
            //计算剩余的等待时间
            long delay = millis - now;
            if (delay <= 0) {//如果剩余的等待时间小于等于0,则终止等待
                break;
            }
            //等待指定时间
            wait(delay);
            //获取当前时间
            now = System.currentTimeMillis() - base;
        }
    }
}

从源码中可以得知,如果要join正常生效,调用join方法的对象必须已经调用了start()方法且并未进入终止状态。

作者:a158123 发表于2017/11/25 19:51:00 原文链接
阅读:40 评论:0 查看评论
Viewing all 35570 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>