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

解决安装Liunx后Windows引导丢失的问题

$
0
0

修改 /boot/grub2/grub.cfg文件中的内容

vim  /boot/grub2/grub.cfg

切换为编辑模式

在### END /etc/grub.d/00_header ###之后添加:
### BEGIN /etc/grub.d/30_os-prober ###

menuentry 'Windows 10 (loader) (on /dev/sda1)' --class windows --class os $menuentry_id_option 'osprober-chain-140E68540E6830C2' {
insmod part_msdos
insmod ntfs
set root='hd0,msdos1'
chainloader +1
}
 ### END /etc/grub.d/30_os-prober ###
作者:baishuiniyaonulia 发表于2017/11/23 16:46:45 原文链接
阅读:116 评论:0 查看评论

C++进阶—>Socket通信那点事

$
0
0

1、网络中进程之间如何通信?

本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类:

  • 消息传递(管道、FIFO、消息队列)
  • 同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)
  • 共享内存(匿名的和具名的)
  • 远程过程调用(Solaris门和Sun RPC)

但这些都不是本文的主题!我们要讨论的是网络中进程之间如何通信?首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址可以唯一标识网络中的主机,而传输层的“协议+端口可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX  BSD的套接字(socket)和UNIX System V的TLI(已经被淘汰),来实现网络进程之间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是我为什么说“一切皆socket”。

2、什么是Socket?

上面我们已经知道网络中的进程是通过socket来通信的,那什么是socket呢?socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭),这些函数我们在后面进行介绍。

3、socket的基本操作

既然socket是“open—write/read—close”模式的一种实现,那么socket就提供了这些操作对应的函数接口。下面以TCP为例,介绍几个基本的socket接口函数。

3.1、socket()函数

int socket(int domain, int type, int protocol);

socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。

正如可以给fopen的传入不同参数值,以打开不同的文件。创建socket的时候,也可以指定不同的参数创建不同的socket描述符,socket函数的三个参数分别为:

  • domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INETAF_INET6AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
  • type:指定socket类型。常用的socket类型有,SOCK_STREAMSOCK_DGRAMSOCK_RAWSOCK_PACKETSOCK_SEQPACKET等等(socket的类型有哪些?)。
  • protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCPIPPTOTO_UDPIPPROTO_SCTPIPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议(这个协议我将会单独开篇讨论!)。

注意:并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。

当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()listen()时系统会自动随机分配一个端口。

3.2、bind()函数

正如上面所说bind()函数把一个地址族中的特定地址赋给socket。例如对应AF_INETAF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

函数的三个参数分别为:

  • sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。
  • addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,如ipv4对应的是: 
    struct sockaddr_in {
        sa_family_t    sin_family; /* address family: AF_INET */
        in_port_t      sin_port;   /* port in network byte order */
        struct in_addr sin_addr;   /* internet address */
    };
    
    /* Internet address. */
    struct in_addr {
        uint32_t       s_addr;     /* address in network byte order */
    };
    ipv6对应的是: 
    struct sockaddr_in6 { 
        sa_family_t     sin6_family;   /* AF_INET6 */ 
        in_port_t       sin6_port;     /* port number */ 
        uint32_t        sin6_flowinfo; /* IPv6 flow information */ 
        struct in6_addr sin6_addr;     /* IPv6 address */ 
        uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */ 
    };
    
    struct in6_addr { 
        unsigned char   s6_addr[16];   /* IPv6 address */ 
    };
    Unix域对应的是: 
    #define UNIX_PATH_MAX    108
    
    struct sockaddr_un { 
        sa_family_t sun_family;               /* AF_UNIX */ 
        char        sun_path[UNIX_PATH_MAX];  /* pathname */ 
    };
  • addrlen:对应的是地址的长度。

通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。

3.3、listen()、connect()函数

如果作为一个服务器,在调用socket()bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。

int listen(int sockfd, int backlog);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

listen函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。

3.4、accept()函数

TCP服务器端依次调用socket()bind()listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()connect()之后就想TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。

注意:accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。

3.5、read()、write()等函数

万事具备只欠东风,至此服务器与客户已经建立好连接了。可以调用网络I/O进行读写操作了,即实现了网咯中不同进程之间的通信!网络I/O操作有下面几组:

  • read()/write()
  • recv()/send()
  • readv()/writev()
  • recvmsg()/sendmsg()
  • recvfrom()/sendto()

我推荐使用recvmsg()/sendmsg()函数,这两个函数是最通用的I/O函数,实际上可以把上面的其它函数都替换成这两个函数。它们的声明如下:

       #include <unistd.h>

       ssize_t read(int fd, void *buf, size_t count);
       ssize_t write(int fd, const void *buf, size_t count);

       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t send(int sockfd, const void *buf, size_t len, int flags);
       ssize_t recv(int sockfd, void *buf, size_t len, int flags);

       ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
       ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);

       ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
       ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

read函数是负责从fd中读取内容.当读成功时,read返回实际所读的字节数,如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误。如果错误为EINTR说明读是由中断引起的,如果是ECONNREST表示网络连接出了问题。

write函数将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数。失败时返回-1,并设置errno变量。 在网络程序中,当我们向套接字文件描述符写时有俩种可能。1)write的返回值大于0,表示写了部分或者是全部的数据。2)返回的值小于0,此时出现了错误。我们要根据错误类型来处理。如果错误为EINTR表示在写的时候出现了中断错误。如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接)。

其它的我就不一一介绍这几对I/O函数了,具体参见man文档或者baidu、Google,下面的例子中将使用到send/recv。

3.6、close()函数

在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。

#include <unistd.h>
int close(int fd);

close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。

注意:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。

3.7、其余函数补充

    上述内容均转自:http://www.cnblogs.com/skynet/,下面为对其socket相关函数的补充。

int send( SOCKET s, const char FAR *buf, int len, int flags ); //面连连接的通信发送数据

第一个参数指定发送端套接字描述符;
第二个参数指明一个存放应用程序要发送数据的缓冲区;
第三个参数指明实际要发送的数据的字节数,即长度;
第四个参数一般置0。

int recv( SOCKET s, char FAR *buf, int len, int flags );  //面向连接的通信接受数据

第一个参数指定接收端套接字描述符;
第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
第三个参数指明buf的长度;
第四个参数一般置0。

int sendto(int sockfd, const void *msg,int len, unsigned int flags, const struct sockaddr *to, int tolen);  //面向无连接的通信发送数据

第一个参数指定发送端套接字描述符;
第二个参数指明一个存放应用程序要发送数据的缓冲区;
第三个参数指明实际要发送的数据的字节数,即长度;
第四个参数一般置0;
第五个参数为存储目的地的IP地址和端口的地址结构体,一般使用sockaddr_in结构体然后转成sockaddr*类型;
第六个参数为sizeof(sockaddr)。

int recvfrom(int sockfd,void *buf,int len,unsigned int lags,struct sockaddr *from,int *fromlen);   //面向无连接的通信接受数据

第一个参数指定接受端套接字描述符;
第二个参数指明一个存放应用程序要接受数据的缓冲区;
第三个参数指明要接受数据的字节数,即长度;
第四个参数一般置0;
第五个参数为存储源地址的IP地址和端口的地址结构体,一般使用sockaddr_in结构体然后转成sockaddr*类型;
第六个参数为sizeof(sockaddr)。

4、socket中TCP的三次握手建立连接详解

我们知道tcp建立连接要进行“三次握手”,即交换三个分组。大致流程如下:

  • 客户端向服务器发送一个SYN J
  • 服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
  • 客户端再想服务器发一个确认ACK K+1

只有就完了三次握手,但是这个三次握手发生在socket的那几个函数中呢?请看下图:

image

图1、socket中发送的TCP三次握手

从图中可以看出,当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。

总结:客户端的connect在三次握手的第二个次返回,而服务器端的accept在三次握手的第三次返回。

5、socket中TCP的四次握手释放连接详解

上面介绍了socket中TCP的三次握手建立过程,及其涉及的socket函数。现在我们介绍socket中的四次握手释放连接的过程,请看下图:

image

图2、socket中发送的TCP四次握手

图示过程如下:

  • 某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
  • 另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;
  • 一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;
  • 接收到这个FIN的源发送端TCP对它进行确认。

这样每个方向上都有一个FIN和ACK。

6、关于socket通信中的个人学习及理解随笔

6.1TCP/IP通信示意图


6.2 bind何时用

(1)采用TCP通信时,客户端不需要bind()他自己的IP和端口号,而服务器必须要bind()自己本机的IP和端口号;
(2)若采用UDP通信时(这里是有客户端和服务器之分才这么说的,若是指定特定端口的UDP对等通信则不一样了),客户端也可以不需要bind()他自己的IP和端口号,而服务器需要bind自己IP地址和端口号;

6.3 socket套接字对象有啥用

在socket编程之前,需要调用socket函数创建一个socket对象,该函数返回该socket对象的描述符。为什么每次都需要一个socket对象及socket对象有啥用。关于socket可以参考《struct socket 结构详解》文章,socket结构体如下:

struct socket   
{   
    socket_state              state;   
    unsigned long             flags;   
    const struct proto_ops    *ops;   
    struct fasync_struct      *fasync_list;   
    struct file               *file;   
    struct sock               *sk;   
    wait_queue_head_t         wait;   
    short                     type;   
};
其中,struct sock 包含有一个 sock_common 结构体,而sock_common结构体又包含有struct inet_sock 结构体,而struct inet_sock 结构体的部分定义如下:
struct inet_sock   
{   
    struct sock     sk;   
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)   
    struct ipv6_pinfo   *pinet6;   
#endif   
    __u32           daddr;          //IPv4的目的地址。   
    __u32           rcv_saddr;      //IPv4的本地接收地址。   
    __u16           dport;          //目的端口。   
    __u16           num;            //本地端口(主机字节序)。  
    …………      
}
由此可知,socket对象即相当于网络通讯中的数据包,里面包含了网络通信的各种相关数据、协议等信息,当然源地址、端口和目的地址、端口也包含在其中。简单的理解就把socket当作网络通信传输的数据包,在编程时是必须的 若不正确定义socket会导致程序错误。

6.4 connect函数干了啥

在TCP客户端,首先调用一个socket()函数,得到一个socket描述符s,然后通过connect函数对服务器进行连接,连接成功后,就可以利用这个s描述符使用send/recv函数收发数据了。对于为什么在connect之后使用send/recv收发数据不需要输入源、目的地址和端口,因为在connect函数调用之后将源地址、端口和目的地址、端口存储在对应的socket中了,至于socket中除此之外还存储了哪些东西 这里不做深究,感兴趣的可以研究此函数源码。

6.5 accept函数产生socket占没占新端口

/* 参数:sockfd 监听套接字,即服务器端创建的用于listen的socket描述符。  
 * 参数:addr  这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址  
 * 参数:len 描述 addr 的长度  
 */ 
int accept(int sockfd, struct sockaddr* addr, socklen_t* len)
accept函数主要用于服务器端,一般位于listen函数之后,默认会阻塞进程,直到有一个客户请求连接,建立好连接后,它返回的一个新的套接字 socketfd_new ,此后,服务器端即可使用这个新的套接字socketfd_new与该客户端进行通信,而sockfd 则继续用于监听其他客户端的连接请求。

毫无疑问accept函数所产生的accept函数所产生的socket套接字套接字并未占用原套接字的端口,这是因为一个端口只能绑定到一个socket中若再绑定到别的socket中则会报错;accept函数所产生的socketfd_new套接字只是复制了原监听套接字sockfd 里面的相关信息而已,并未与端口进行绑定。当客户端与服务端进行通信的时候socketfd_new套接字接受并与之通信。而对于连接服务器端的请求则使用的是通过bind函数绑定的socket套接字与其建立连接。

6.6 UDP通信嘚吧嘚

UDP通信是无连接的通信,因此其通信速度更快,但由于其没有想TCP/IP那种数据容错机制,若通信不佳,则往往UDP在数据通信的时候会导致数据丢失;实时通信上使用的是UDP协议,如QQ、微信等,数据传输中则使用的是TCP协议,如文件传输、视频在线播放等。


创建UDP通信的套接字函数往往如下:

m_udpSocket=socket(AF_INET,SOCK_DGRAM,0); //SOCK_DGRAM为UDP流 SOCK_STREAM为TCP流


UDP通信的时候无需绑定端口和地址(当然在UDP通信时存在服务器的情况下 服务器是需要使用bind绑定的),在通信时要指定发送者或接受者的地址和端口,函数如下:

int sendto(int sockfd, const void *msg,int len, unsigned int flags, const struct sockaddr *to, int tolen);  //面向无连接的通信发送数据
第一个参数指定发送端套接字描述符;
第二个参数指明一个存放应用程序要发送数据的缓冲区;
第三个参数指明实际要发送的数据的字节数,即长度;
第四个参数一般置0;
第五个参数为存储目的地的IP地址和端口的地址结构体,一般使用sockaddr_in结构体然后转成sockaddr*类型;
第六个参数为sizeof(sockaddr)。
int recvfrom(int sockfd,void *buf,int len,unsigned int lags,struct sockaddr *from,int *fromlen);   //面向无连接的通信接受数据
第一个参数指定接受端套接字描述符;
第二个参数指明一个存放应用程序要接受数据的缓冲区;
第三个参数指明要接受数据的字节数,即长度;
第四个参数一般置0;
第五个参数为存储接收到的源地址的IP地址和端口的地址结构体,一般使用sockaddr_in结构体然后转成sockaddr*类型;
第六个参数为sizeof(sockaddr)。


使用UDP通信的流程较为简单大致步骤如下:

/***********************下面函数是发送UDP数据的***********************/

        char hostname[50];
	int Result;
	Result=gethostname(hostname,50);   //获取本地主机名给hostname
	if(Result!=0)
	{
		MessageBox("主机查找错误!","Error!",MB_OK);
		return FALSE;
	}
	HOSTENT* hst=NULL;
	CString strTemp;
	struct in_addr ia; 

	hst = gethostbyname((LPCTSTR)hostname);	//通过主机名获得本地主机相关设备信息给hst


	memcpy(&ia.s_addr,hst->h_addr_list[0],sizeof(ia.s_addr)); //将本机的ip地址拷给ia结构体

	SOCKADDR_IN m_Addr   //定义存储地址和端口的结构体m_Addr
        m_Addr.sin_addr=ia;
	m_Addr.sin_family=AF_INET;
	m_Addr.sin_port=htons(1234);

        m_sendSocket = socket(AF_INET,SOCK_DGRAM,0)  //定义UDP套接字
        int Result=sendto(m_sendSocket,m_msg,m_msg.GetLength(),0,(sockaddr*)&m_SevAddr,sizeof(SOCKADDR)); //发送数据m_msg给目的地址
	if(Result==SOCKET_ERROR)
	{
		MessageBox("信息发送失败!");
		return;

	}
/***********************下面函数是接受UDP数据的***********************/
       char buf[30];
       SOCKADDR_IN AddrMsgSend;
	int len=sizeof(SOCKADDR);
	SOCKET  m_socket = socket(AF_INET,SOCK_DGRAM,0);
	int result;
        result=recvfrom(m_socket,buf,30,0,(sockaddr*)&AddrMsgSend,&len);
	if(result!=SOCKET_ERROR)
		MessageBox("Error!");


当然为了省去每次发送数据都要输入目标地址和端口的麻烦,可以使用connect函数进行连接,这样与目的地址和端口连接之后即可使用send和secv函数进行数据的收发了!












作者:u011028345 发表于2017/11/23 17:37:04 原文链接
阅读:154 评论:0 查看评论

JQuery DataTables 导出 Excel(文件导出,打印功能)

$
0
0

JQuery DataTables 的Button框架(扩展)中 内置按钮 为 JQuery DataTables 提供了 文件导出打印列可见性功能。

本篇博文,就以我们常用的 文件导出 功能为例,介绍一下 内置按钮功能。

文件导出

下面给出了官网对文件导出功能的翻译, 如果大家不想读,我就简单给大家总结一下:

  1. 文件导出,框架提供了两种实现方式: 1. HTMT5 2. FLASH ;
  2. H5 可以适应现代浏览器,而FLASH 则可兼容老版本浏览器,但是要依赖于FLASH插件, 厉害的来了, 框架可以自动 检测浏览器能力,帮我们选择适合的导出方式。(这是很棒的)
  3. 引入文件导出需要的JS后,我们可以使用四种扩展 ,分别是: copy, csv, excel, pdf

在DataTable中显示数据时,最终用户通常可以使用这些数据来获取DataTable中的数据,并将其提取到文件中以供本地使用。这可以使用基于HTML5的按钮或Flash按钮来完成。

按钮有 四个内置的按钮类型,它们将 自动检测浏览器的能力和可用的软件 - 如果可能的话,它们将自动使用HTML5按钮,如果没有满足要求,则会回落到Flash并最终不显示

  • copy - 复制到剪贴板
  • csv - 保存到CSV文件
  • excel - 保存到Excel XLSX文件
  • pdf - 保存为PDF文档

——————–摘自 官网翻译

文件导出实现

上面已经介绍了理论知识,下面我们介绍一下,具体怎么实现。

上面已经说到了,插件不光支持 导出Excel,并且支持其他形式的导出

一、引入必要的JS扩展

扩展功能,并没有和核心库放在一起,所以我们要单独引入。

直接选择我们要使用的扩展,而后打包下载 (*推荐)https://datatables.net/download/
(这个我们在前面自定义按钮时,提到过,我推荐大家打包下载。)

如图勾选如下扩展, 而后可选择,压缩并合并,提升系统性能。

Buttons

补充
官网还提供了其他引入方式

其他下载方式

当然大家也可以选择使用自己的资源,那么请大家引入以下资源

link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.css"/>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/buttons/1.4.2/css/buttons.dataTables.css"/>

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jszip/2.5.0/jszip.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.32/vfs_fonts.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.32/pdfmake.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.4.2/js/dataTables.buttons.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.4.2/js/buttons.flash.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.4.2/js/buttons.html5.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.4.2/js/buttons.print.js"></script>

二、使用Button 按钮扩展功能

//再引入已上资源之后,我们便可以直接使用以下功能了
//参考文档: http://www.datatables.club/extensions/buttons/

//例子1:
$('#myTable').DataTable( {
    buttons: [
        'copy', 'excel', 'pdf'
    ]
} );

//例子2: 导出Excel
$('#myTable').DataTable( {
    buttons: [
        {
            extend: 'excel',//使用 excel扩展
            text: '导出本页',// 显示文字
            exportOptions: {
                //自定义导出选项
                //如:可自定义导出那些列,那些行
                //TODO...
            }
        }
    ]
} );

三、 补充: 自定义导出数据

由于默认是将整个表格的数据导出,这显然可能不是我们想要的,

比如:我们并不想要导出 checkbox,操作等列。所有我们可以通过配置,自定义导出那些数据。

我们已 Excel 扩展为例

Execl 扩展 有如下可选配置:

官网文档: https://datatables.net/reference/button/excel

配置说明

重点是:exportOptions
官网文档: https://datatables.net/reference/api/buttons.exportData()

这里写图片描述

我用红线标出来是列选择器,也就是我要给大家做的例子,打印指定列。

我们可以参考官网文档(列选择器):https://datatables.net/reference/type/column-selector

我们又多种方式控制要选择的列: 比如索引,列名,类名,节点,JQuery等多种方式去选择我们要打印的列。

配置实例:

//通过索引直接选择要打印的多列
$('#example').DataTable( {
    dom: 'Bfrtip',
    buttons: [
        {
            extend: 'excel',
            exportOptions: {
                columns: [ 0, 1, 2, 5 ]
            }
        },
    ]
} );

//通过Name控制要打印的列
//格式为 [列名]: name
$('#example').DataTable( {
    dom: 'Bfrtip',
    buttons: [
        {
            extend: 'excel',
            exportOptions: {
                // 将打印 id 和 title 列
                columns: [ 'id:name','title:name']
            }
        },
    ]
} );

参考博文

Button 按钮的初始化: http://www.datatables.club/extensions/buttons/
Button 扩展下载: https://datatables.net/download/
文件导出配置: https://datatables.net/reference/button/excel
自定义导出数据配置:https://datatables.net/reference/api/buttons.exportData()
列的选择: https://datatables.net/reference/type/column-selector

作者:shuai_wy 发表于2017/11/23 17:54:06 原文链接
阅读:158 评论:0 查看评论

(13)intellij plugins-- markdown

$
0
0
  • what is markdown?
      It’s defined on Wikipedia.https://en.wikipedia.org/wiki/Markdown
      as its core,markdown is a super simple way to add fomatting like headers,bold,bulleted lists ,and so on to plain text. It was originally desinged to be an easy altermative to Html, and allows people to create web pages with no html experience— but it’s also a great way to organize notes, to-do list ,and other things. it’s has all the advantages fo plain text, but with the organizational power of a word processor. The end goal is a minimalist writing system that you can use to get your thoughts down ,and then export them elsewhere without worrying too mush about the appearance.
  • how to install markdown in intellij?
      use intellij shortcut ctrl + shif + s or select tool bar of Setting button. Select Plugins tab and select the Browse Repositories button, input “markdown” and search,following the picture:

    intellij markdown
      we will see two markdown plugins, i thing the free one is better,we don’s have to crack it . So we download and install “Markdown support”
      restart the intellij , and creat a new file with file suffix “.md”, and open it in intellij .

  • how to use markdown in intellij?
      the bolg is very useful for us , https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#headers
      it’s easy to use markdown

作者:lovesummerforever 发表于2017/11/23 18:01:00 原文链接
阅读:113 评论:0 查看评论

Qt--XML

$
0
0

XML是一种可扩展的标签语言,常用于数据保存和交互。

Qt中提供了几种方法可对xml进行解析,DOM、SAX还有core模块提供的QXmlStreamReader和QXmlStreamWrite。

DOM是分析一个小型xml的选择,它将整个xml解析成一个对象树结构,我们只需要对解析结构遍历即可。
对于大型xml则使用QXmlStreamReader更加有速度和内存优势。

对于一般应用程序,DOM更加简单实用,下面给出示例

xml文件内容:

<?xml version="1.0" encoding="UTF-8" ?>
<director_service>
    <layout>
        <head>
            <param n="debug" v="1" />

            <param n="autolayout" v="0" />
            <param n="row" v="0" />
            <param n="col" v="0" />

            <param n="width"  v="1280" />
            <param n="height" v="800"  />

            <param n="audio"        v="1" />
            <param n="display_mode" v="2" />
            <param n="scale_mode" v="1" />
            <param n="fps"      v="15" />

            <param n="drawtitle" v="1" />
            <param n="drawfps" v="1" />
            <param n="drawnum" v="1" />
            <param n="drawaudio" v="1" />
            <param n="drawinfo" v="0" />
        </head>
        <body>
            <item>
                <param n="u"   v="1" />
                <param n="w"   v="424" />
                <param n="h"   v="264" />
                <param n="x"   v="4"   />
                <param n="y"   v="4"   />
            </item>
            <item>
                <param n="u"   v="2"   />
                <param n="w"   v="424" />
                <param n="h"   v="264" />
                <param n="x"   v="4"   />
                <param n="y"   v="268" />
            </item>
            <item>
                <param n="u"   v="3"   />
                <param n="w"   v="424" />
                <param n="h"   v="264" />
                <param n="x"   v="4"   />
                <param n="y"   v="532" />
            </item>
            <item>
                <param n="u"   v="4"   />
                <param n="w"   v="424" />
                <param n="h"   v="264" />
                <param n="x"   v="428" />
                <param n="y"   v="532" />
            </item>
            <item>
                <param n="u"   v="5"   />
                <param n="w"   v="424" />
                <param n="h"   v="264" />
                <param n="x"   v="852" />
                <param n="y"   v="532" />
            </item>
            <item>
                <param n="u"   v="6"   />
                <param n="w"   v="848" />
                <param n="h"   v="528" />
                <param n="x"   v="428" />
                <param n="y"   v="4"   />
            </item>                                     
        </body>     
    </layout>
</director_service>

Qt的QDomDocment示例:

#include <QtXml/QDomComment>
int HDsContext::parse_layout_xml(const char* xml_file){
    qDebug(xml_file);

    QDomDocument dom;
    QFile file(xml_file);
    QString err;
    if (!dom.setContent(&file, &err)){
        qWarning("parse_layout_xml failed:%d", err.toLocal8Bit().data());
        return -1;
    }

    QDomElement elem_root = dom.documentElement();
    QDomElement elem_layout = elem_root.firstChildElement("layout");
    if (elem_layout.isNull())
        return -2;
    QDomElement elem_head = elem_layout.firstChildElement("head");
    if (elem_head.isNull())
        return -3;
    QDomElement elem_body = elem_layout.firstChildElement("body");
    if (elem_body.isNull())
        return -4;

    QDomElement elem_param = elem_head.firstChildElement("param");
    while (!elem_param.isNull()) {
        QString n = elem_param.attribute("n");
        QString v = elem_param.attribute("v");
        if (n == "debug"){
            m_tInit.debug = v.toInt();
        }else if (n == "autolayout"){
            m_tInit.autolayout = v.toInt();
        }else if (n == "row"){
            m_tInit.row = v.toInt();
        }else if (n == "col"){
            m_tInit.col = v.toInt();
        }else if(n == "width"){
            m_tLayout.width     = v.toInt();
        }else if(n == "height"){
            m_tLayout.height    = v.toInt();
        }else if (n == "audio"){
            m_tInit.audio = v.toInt();
        }else if(n == "fps"){
            m_tInit.fps = v.toInt();
        }else if (n == "display_mode"){
            m_tInit.display_mode = v.toInt();
        }else if (n == "scale_mode"){
            m_tInit.scale_mode = v.toInt();
        }else if (n == "drawtitle"){
            m_tInit.drawtitle = v.toInt();
        }else if (n == "drawfps"){
            m_tInit.drawfps = v.toInt();
        }else if (n == "drawnum"){
            m_tInit.drawnum = v.toInt();
        }else if (n == "drawaudio"){
            m_tInit.drawaudio = v.toInt();
        }else if(n == "drawinfo")
            m_tInit.drawinfo     = v.toInt();
        else if(n == "infcolor")
            m_tInit.infcolor      = v.toInt(NULL, 16);
        else if(n == "titcolor")
            m_tInit.titcolor      = v.toInt(NULL, 16);

        elem_param = elem_param.nextSiblingElement("param");
    }

    QDomElement elem_item = elem_body.firstChildElement("item");
    int cnt_item = 0;
    int x,y,w,h;
    while (!elem_item.isNull()){
        QDomElement elem_param = elem_item.firstChildElement("param");
        while (!elem_param.isNull()){
            QString n = elem_param.attribute("n");
            QString v = elem_param.attribute("v");
            if(n == "w")
                w = v.toInt();
            else if(n == "h")
                h = v.toInt();
            else if(n == "x")
                x = v.toInt();
            else if(n == "y")
                y = v.toInt();

            elem_param = elem_param.nextSiblingElement("param");
        }

        m_tLayout.items[cnt_item].setRect(x, y, w, h);

        ++cnt_item;
        if(cnt_item >= MAXNUM_LAYOUT)
            break;

        elem_item = elem_item.nextSiblingElement("item");
    }

    m_tLayout.itemCnt = cnt_item;

    return 0;
}

QDomDocument 的setContent方法可以解析I/O流或者字符串。
documentElement返回根元素
QDomElement中提供了firstChildElement查找子元素,nextSiblingElement遍历兄弟元素,
attribute返回属性字符串

作者:GG_SiMiDa 发表于2017/11/23 19:26:02 原文链接
阅读:134 评论:0 查看评论

NOIp2017游记

$
0
0

终于回机房啦哈哈哈

Day1

T1

传说中的小学奥数题。。。

刚开始看了一眼——什么???数学题???果断跳过。。。

把T2和T3打完以后回来看题。怎么办啊不会啊。。。果断打表。。。

打了十几分钟发现了规律,立刻码完。

考完后发现自己的规律和别人的不一样,顿时虚了起来。自己化简了一下式子后发现是一回事。

贴一下自己找到的鬼畜规律:

if (a>b) swap(a,b);
LL ans=(4+(a-3)*2+4)*(a-2)/2+1;//a=1和a=2的情况已经预处理
ans+=(b-a-1)*(a-1);

T2

大模拟啊!

要不是大样例给的良心估计这道题我就挂了,还调了两个小时。。。自己还是太菜。

然而最后还是没打满,一个小细节出了问题。

T3

T2打完以后发现还剩一个半小时,保险起见就打了10分(然而测出来有30)的暴力。直接最短路计数。

考完以后同学说正着倒着都跑最短路一遍,那么dis[1][i]+dis[i][n]的点就可以直接去掉。这样就有60了。。。

标算好像是套路分层图DP,然而我不会啊。

Day1:100+80+30=210

Day2

T1

刚开始被三维坐标系吓到了。。。然而还是常规水题,n2建边,bfs跑一边就好了。

但是会爆long long!还好出题人不卡。

T2

把题目想简单了,直接打了一个prim,还美滋滋的以为自己对了。还剩一个小时的时候才发现是错的。一看n=12就想到了状压DP,然而并不会打。只能弃掉。理论得分40,实际得分45。(好像并没有什么区别)

T3

实在不会做,数据结构太弱。打了30分的暴力。

manchery直接平衡树艹掉了啊!Orz

Day2:100+45+30=175

总分:210+175=385,压线ZJ一等。。。

作者:a1799342217 发表于2017/11/23 20:28:43 原文链接
阅读:155 评论:0 查看评论

arcgis api for js热力图优化篇-不依赖地图服务

$
0
0

前面我写过一篇文章,介绍如何实现arcgis api的热力图效果,但是依赖arcgis server发布的地图服务来获取热力图的数据源。实际应用中,很多业务数据来源数据库,并不一定是从地图服务来获取的。所以,本篇文章从两个不同的角度来优化一下热力图,谈谈不一样的实现热力图思路。

1.arcgis api的FeatureLayer构造数据源不同之处来实现热力图:

构造FeatureLayer的数据源是通过自己模拟数据或者从数据库读取数据

首先,构造FeatureSet:

复制代码
var featureSet = new esri.tasks.FeatureSet(dz);
            var layerDefinition = {
                    "geometryType": "esriGeometryPoint",
                    "fields": [
                               {
                                "name": "勘探深度",
                                "type": "esriFieldTypeDouble",
                                "alias": "勘探深度"
                               },
                               {
                                "name": "孔口高程",
                                "type": "esriFieldTypeDouble",
                                "alias": "孔口高程"
                               },
                               {
                                "name": "X",
                                "type": "esriFieldTypeDouble",
                                "alias": "X"
                               },
                               {
                                "name": "Y",
                                "type": "esriFieldTypeDouble",
                                "alias": "Y"
                               },
                               {
                                "name": "水位高程",
                                "type": "esriFieldTypeDouble",
                                "alias": "水位高程"
                               }
                              ]
             } 
var featureCollection = {
                layerDefinition: layerDefinition,
                featureSet: featureSet
};
复制代码

实现效果如下:

 

2.开源heatmap.js结合arcgis api for js实现热力图:

自定义HeatmapLayer类,继承DynamicMapServiceLayer,然后结合heatmap.js一起

复制代码
        /**
         * 创建热力图
         * 依赖开源js库heatmap.js
        */
        createHeatMapByJS:function(map,featureSet){
            // create heat layer
            var heatLayer = BX.heatmap.heatLayer2 = new heatmap.HeatmapLayer({
                "useLocalMaximum": false,
                config: {
                    "radius": 40,
                    "valueField": "水位高程",
                    "gradient": {
                        0.45: "rgb(000,000,255)",
                        0.55: "rgb(000,255,255)",
                        0.65: "rgb(000,255,000)",
                        0.95: "rgb(255,255,000)",
                        1.00: "rgb(255,000,000)"
                    }
                },
                "map": map,
                "opacity": 0.85
            }, "heatLayerDIV"); 
            // set heatmap data
            heatLayer.setData(featureSet.features);
            // add heat layer to map
            map.addLayer(heatLayer);            
            //heatLayer.show();         
            
        }
复制代码

最终实现热力图效果如下:

备注:团队承接webgis/gis毕业设计以及webgis项目等业务,欢迎有相关需求的客户来咨询
GIS之家接受webgis开发遇到的技术疑点难点在线咨询,采取在线分答计时收费模式,有需要的加QQ:406503412
欢迎关注我的GIS之家微信公众号(扫描右上角头像):gishome
GIS之家论坛(推荐):GIS之家论坛
GIS作品:GIS之家
GIS之家知乎专栏:GIS之家知乎专栏
GIS之家交流群一:432512093(已满)
GIS之家交流群二:296438295(已满)
GIS之家交流群三:632467934
作者:liguoweioo 发表于2017/11/23 20:53:21 原文链接
阅读:119 评论:0 查看评论

Java并发编程札记-(一)基础-07volatile详解

$
0
0

volatile同synchronized一样,是Java中实现线程安全的一种机制。与synchronized相比,特点是使用简单、性能高,但容易出错、使用范围有限。

《Java语言规范(第三版)》中讲到: Java允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。Java提供了volatile,在某些情况下比锁更加方便。如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的。

Java中的volatile可以看做是“轻量级的synchronized”。synchronized可能会引起上下文切换和线程调度,同时保证可见性、有序性和原子性。volatile不会引起上下文切换和线程调度,但仅提供可见性和有序性保证,不提供原子性保证。

目录:

  1. 相关概念
  2. synchronized锁与volatile变量的比较
  3. volatile的使用场景

相关概念

上文中提到,volatile保证可见性和有序性,不保证原子性。这里有几个概念需要了解下。

原子操作与原子性
如果一系列(或者一个)操作是不可中断的,要么都执行,要么不执行,就称操作是原子操作,具有原子性。
拿移动支付举例,A用户向B用户付款100元,其中包含两个操作:A用户账户扣减100元,B用户账户增加100元。如果这两个操作不是原子操作就可能会出错,比如A账户账户扣减100元,但B用户账户并没有增加100元。

可见性
可见性指的是多个线程对共享资源的可见性。当一个线程修改了某一资源,其他线程能够看到修改结果。

有序性
有效性指程序按照代码的先后顺序执行。

synchronized锁与volatile变量的比较

  • volatile变量最大的优点是使用方便。在某些情形下,使用volatile变量要比使用相应的synchronized锁简单得多。
  • 某些情况下,volatile变量同步机制的性能要优于synchronized锁。
  • volatile变量不会像synchronized锁一样造成阻塞。
  • volatile变量最大的缺点在于使用范围有限,而且容易出错。

总的来说volatile变量使用范围有限,不能替代synchronized,但在某些场景下,使用volatile更好。

volatile的使用场景

关于volatile的使用场景推荐阅读正确使用 Volatile 变量一文。

volatile还是比较难理解的,本文只是简单地做了介绍,以后随着理解的加深会继续完善本文。
本文暂时就讲到这里,想了解Java并发编程更多内容请参考:

待完善。

作者:panweiwei1994 发表于2017/11/23 21:00:20 原文链接
阅读:114 评论:0 查看评论

BZOJ1058(ZJOI2007)报表统计--STL

$
0
0

【链接】
bzoj1058

【解题报告】

好像是平衡树裸题。但时限15s,stl乱搞就过了。

其实就只需记录每个位置的第一个数和最后一个数就可以确定最小值,然后stl乱搞就行了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
const int maxn=500005,INF=1<<30;
int n,m,MIN,a[maxn],st[maxn],gl[maxn];
set <int> S,B;
map <int,int> mp;
inline char nc()
{
    static char *l,*r,buf[100000];
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; return *l++;
}
inline int Read()
{
    int res=0,f=1; char ch=nc(),cc=ch;
    while (ch<'0'||ch>'9') cc=ch,ch=nc();
    if (cc=='-') f=-1;
    while (ch>='0'&&ch<='9') res=res*10+ch-48,ch=nc();
    return res*f;
}
int Abs(int x) {if (x<0) return (-x); return x;}
void Insert(int x) {mp[x]++; if (mp[x]==1) B.insert(x);}
void Push(int where,int p)
{
    if (where<n) {int t=Abs(gl[where]-st[where+1]); mp[t]--; if (!mp[t]) B.erase(t);}
    Insert(Abs(p-gl[where])); Insert(Abs(p-st[where+1]));
    int x=*--S.lower_bound(p),y=*S.lower_bound(p);
    MIN=min(MIN,min(Abs(p-x),Abs(p-y)));
    S.insert(p); gl[where]=p; 
}
int main()
{
    freopen("1058.in","r",stdin);
    freopen("1058.out","w",stdout);
    n=Read(); m=Read(); MIN=INF;
    while (!B.empty()) B.clear();
    while (!S.empty()) S.clear();
    S.insert(INF); S.insert(-INF);
    for (int i=1; i<=n; i++) a[i]=st[i]=gl[i]=Read(),S.insert(st[i]);
    sort(a+1,a+1+n); for (int i=2; i<=n; i++) Insert(Abs(st[i]-st[i-1])),MIN=min(MIN,Abs(a[i]-a[i-1]));
    for (int i=1; i<=m; i++)
    {
        char ch=nc();
        while (ch!='I'&&ch!='M') ch=nc();
        if (ch=='I')
         {
            int x=Read(),p=Read();
            Push(x,p); continue;
         }
        nc(); nc(); nc(); ch=nc();
        if (ch=='G') printf("%d\n",*B.begin()); else printf("%d\n",MIN);
    }
    return 0;
}
作者:CHNWJD 发表于2017/11/23 21:02:28 原文链接
阅读:104 评论:0 查看评论

HDU1847 Good Luck in CET-4 Everybody!

$
0
0

SG函数

题目传送门

简单SG函数的应用。

sg[i]=sg[i1] xor sg[i2] xor sg[i4] xor  xor sg[i2j](2ji)

然而我在调试的时候发现了一个规律:

sg[i]=i mod 3

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1005
using namespace std;
int n,sg[N],f[N];
int main(){
    for (int i=1;i<N;i++){
        for (int j=1;j<=i;j*=2)
            f[sg[i-j]]=i;
        while (f[sg[i]]==i) sg[i]++;
    }
    while (scanf("%d",&n)==1)
        if (sg[n]) printf("Kiki\n");
        else printf("Cici\n");
    return 0;
}
作者:a1799342217 发表于2017/11/23 21:16:24 原文链接
阅读:132 评论:0 查看评论

使用synchronized实现死锁

$
0
0

死锁简介

死锁定义

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

死锁产生条件

  1. 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
  2. 请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
  3. 不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
  4. 环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

死锁实例

介绍完死锁的产生条件,下面我会用一个使用synchronized实现死锁的实例向大家展示死锁如何产生、排查和处理:

/**
 * 使用synchronized实现一个简单的死锁
 * @author RJH 
 * @date 2017年11月23日 下午8:24:13
 */
public class SynchronizedDeadLock {

    public static void main(String[] args) {
        //定义2个锁对象
        final Object o1 = new Object();
        final Object o2 = new Object();

        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                synchronized (o1) {//获取o1的锁
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (o2) {//获取o2的锁

                    }
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                synchronized (o2) {//获取o2的锁
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (o1) {//获取o1的锁

                    }
                }
            }
        });
        //启动
        t1.start();
        t2.start();
    }
}

运行结果就不用说了,出现了死锁。接下来向大家介绍怎么排查:

死锁排查和分析

  1. Windows环境下打开cmd,输入jps显示Java的进程PID(Linux环境也是一样,在命令行界面输入jps),显示如下结果(我们只要关心SynchronizedDeadLock类的进程即可):

    3828 Jps
    7780
    5800 SynchronizedDeadLock
    
  2. 在jps显示的结果中,左边是进程PID,右边是类名,这次的实例类名为SynchronizedDeadLock,所以找到PID为5800(要根据你自己执行时的PID为准)。然后输入jstack 5800(即jstack PID)显示dump信息(我只截取了关于死锁的部分):

    Java stack information for the threads listed above:
    ===================================================
    "Thread-1":
            at com.rjh.lock.SynchronizedDeadLock$2.run(SynchronizedDeadLock.java:40)
    
            - waiting to lock <0x00000000d5fae710> (a java.lang.Object)
            - locked <0x00000000d5fae720> (a java.lang.Object)
            at java.lang.Thread.run(Thread.java:745)
    "Thread-0":    
            at com.rjh.lock.SynchronizedDeadLock$1.run(SynchronizedDeadLock.java:24)
    
            - waiting to lock <0x00000000d5fae720> (a java.lang.Object)
            - locked <0x00000000d5fae710> (a java.lang.Object)
            at java.lang.Thread.run(Thread.java:745)
    
    Found 1 deadlock.
    
  3. dump分析:在最后一行知道了有一个死锁,而且从截取部分的信息知道,死锁是有Thread-1和Thread-0导致的,Thread-1等待<0x00000000d5fae710>这个对象的锁,并且已经锁定了<0x00000000d5fae720>,而Thread-0等待<0x00000000d5fae720>,并且锁定了<0x00000000d5fae720>这个对象的锁(注意<>内的是对应的对象的内存地址)。由于文字说明可能没怎么直观,还是展示一下简单的分析图(图中的箭头如果是穿过锁,这表示持有锁。否则表示等待持有锁):

synchronized死锁实例分析图

  1. 产生死锁的原因分析:死锁产生需要满足之前介绍的四个条件,我来一一分析:
    1. 互斥条件:从synchronized的定义就可以确定是满足互斥的,毕竟只有一条线程能够执行到。
    2. 请求和保持条件:从dump分析和分析图就知道,两个线程都持有一个锁,且相互在尝试获取对方持有的锁,而且双方都不愿意释放自己持有的锁。
    3. 不剥夺条件:synchronized未执行完,锁不能释放。
    4. 环路等待条件:两个线程很明显形成了环路了。
作者:a158123 发表于2017/11/23 21:23:17 原文链接
阅读:115 评论:0 查看评论

第七章 数据库设计 E-R模型

$
0
0

本章重点放在E-R模型和E-R图,这是这一章节最常用且非常重要的一个知识点,本章的目的就是讲述一个E-R图是怎么画出来的。

什么是E-R模型?

E-R模型是有P.P.S.Chen提出的一个用E-R图描述现实世界的概念模型

E-R模型有三个重要的概念:实体、属性、联系。

实体就是现实世界的一个现实或虚拟的对象,如一个学生就是一个实体,学生有属性 : 学号、姓名、年龄、身高等。属性依附于实体而存在,一个实体通常有一个或多个属性。有学生就有老师,一个老师有多个学生,同样一个学生有多个老师,那么学生与老师存在多对多的联系。一个学生只能存在于一个班级中,一个班级有多个学生,所以学生与班级存在多对一的联系。

E-R图

E-R图是用来表示实体及实体间关系的图,在数据库的设计中被广泛运用。下面我们直接来看一个E-R图的实例,很快就能理解什么是E-R图。

:现在有一个工厂的物资管理的概念模型,工厂中存在如下的实体及其属性:

  • 仓库:仓库号、面积、电话号码。
  • 零件:零件号、名称、规格、单价、描述
  • 供应商:供应商号、姓名、地址、电话号码、账号
  • 项目:项目号、预算、开工日期
  • 职工:职工号、姓名、年龄、职称。

各个实体的联系如下

  1. 仓库与零件:一个仓库有多个零件,一个零件也可存放于多个仓库,所以仓库与零件是多对多联系。
  2. 仓库与职工:一个仓库一个职工,一个职工同时只能在一个仓库上班,所以仓库和职工是一对一联系。
  3. 职工与职工:职工中有一个领导,领导若干下属共同管理仓库,所以职工中存在一对多的联系。
  4. 供应商、项目、零件:一个供应商可以给多个项目供应多种零件,一个项目可以使用多个供应商的多种零件,一种零件可以由多个供应商供给且一种零件可用于多个项目。

分析好这个模型下的实体和实体间的关系后,可以开始画图了!!!(使用office visio)

第一步:画出各个实体。

这里写图片描述

第二步:根据实体间的联系画出实体关系。

这里写图片描述

菱形用来连接多个实体,表示的是实体间的联系,m、n、p表示的是多的意思,供应商与项目是多对多的联系,所以菱形连接这两个实体时,线上的字母符号表示了这种联系。

第三步:在实体联系图的实体上面画出实体的属性。

这里写图片描述

这样,看起来有模有样的E-R图就画好了,可以根据E-R图进行数据库设计啦!!

纯手动画,若有用,请往死里顶我!!

作者:csdn_blog_lcl 发表于2017/11/23 21:35:51 原文链接
阅读:135 评论:0 查看评论

(14) intellij plugins-- ideavim

$
0
0

  ideaVim 插件下载

  • vim相关快捷键

  参考:https://vim.rtorr.com/lang/zh_cn/

  • vim常用快捷键

    vim常见的有两种模式,一种是insert模式,像编辑器一样正常的输入字符编辑字符. 一种是Normal模式,在该模式下可以快速的修改.
    1. 光标的移动
    h,j,k,l : 左 下 上 右
    w: 光标移动到下一个单词的首位
    b:光标移动到当前单词的首位(或者光标移动到光标所在地的本单词或上一个单词首位)
    e:光标移动至当前单词末尾
    gg: 光标移动至文本首行.
    shift + g: 光标移动至文本尾行
    33 + shift + g:光标移动至文本第33行
    2. 插入行
    o: 在当前光标的下方插入一行
    shift + o: 在当前光标的上方插入一行

    3. 删除与恢复
    x: 删除光标后的一个字符.
    shift + x: 删除光标前的一个字符.
    dd:删除光标所在整行,同时被删除行存在于剪切板中.
    de:删除光标后的单词内容, 同时删除的内容在剪切板中.
    dw: 删除光标后的单词内容以及之后的空格, 同时删除的内容在剪切板中.(de和dw区分)
    u:还原上一个操作(不限于删除)
    4. 剪切 复制粘贴
    选定文本块:使用v进入可视模式;移动光标键选定内容
    y:复制光标选定的块
    yy: 复制光标所在的整行
    d: 剪切选定的块
    dd: 剪切光标所在的整行
    p:粘贴文本

    5. 查找替换

    f + o : 在当前行的光标之后查找字母o;(多次f +o,查找下一个o)
    F +b : 在当前行的光标之前查找字母b;
    :/word : 全文查找word. 进入了命令模式, 查找操作支持正则表达式. (n查找下一个 N查找上一个.)
    r + p : 将光标之后的字符替换为字母;
    :s/word/replace : 光标所在行的第一个word替换为replace;
    :%s/old/new/ : 全文查找old并替换为new. (注意to后面有/)
    :%s/old/new/g : 全部替换
    :%s/old/new/gc:全文查找old并替换为new,替换时询问,逐个替换。
    可以选择y/n/a/q/l/^E/^Y:y表示同意当前替换;n表示不同意当前替换;
    a表示替换当前和后面的并且不再确认;
    q表示立即结束替换操作;
    l表示把当前的替换后结束替换操作;
    ^E向上滚屏
    ^Y向下滚屏,用来帮助查看前后内容以决定进行操作。

参考:http://equation85.github.io/blog/markdown-examples/

作者:lovesummerforever 发表于2017/11/23 21:40:23 原文链接
阅读:107 评论:0 查看评论

linux系统sudoers文件夹权限777以及/etc/profile文件修改后无法进入系统问题

$
0
0

有位博友在我的另外一篇文章中留言,关于修改/etc文件夹权限为777导致/etc/sudoers文件夹权限修改为777后无法使用 “sudo”指令,关于这个问题打算与误操作/etc/profile文件导致重启后进不去系统一起解决。

首先说下sudoers文件夹恢复为0440方法:

方法一(直接在图形界面修改):
①进入Terminal 切换到管理员身份,输入su。
如果没有成功切换到管理员身份需要设置一下登录密码:
1.输入passwd指令,提示输入登录密码,然后是设置登录密码(这里可以设置为登录系统时用的密码);
2.输入su。
②输入chmod 0440 /etc/sudoers,没有打印任何信息说明修改成功了(试下sudo指令是否还是提示777);
如果提示README 为777,根据README路径修改为 0440后, 可以使用sudo指令了。

方法二(在命令界面修改):
①首先按键盘alt+ctrl+F1按键切换到命令界面。
② 切换到管理员身份,输入su。
如果没有成功切换到管理员身份需要设置一下登录密码:
1.输入passwd指令,提示输入登录密码,然后是设置登录密码(这里可以设置为登录系统时用的密码);
2.输入su。
③输入chmod 0440 /etc/sudoers,没有打印任何信息说明修改成功了(试下sudo指令是否还是提示777);
如果提示README 为777,根据README路径修改为 0440后, 可以使用sudo指令了。
④按键盘alt+ctrl+F7切换到命令界面。

——————————————————————————————

下面说下/etc/profile文件修改后无法进入系统恢复方法:
①首先按键盘alt+ctrl+F1按键切换到命令界面。
②切换到管理员身份,输入su。
如果没有成功切换到管理员身份需要设置一下登录密码:
1.输入passwd指令,提示输入登录密码,然后是设置登录密码(这里可以设置为登录系统时用的密码);
2.输入su。
③输入cd /etc进入到etc文件夹下;
④使用vim编辑profile文件:
输入vi profile,找到profile文件中误写入(或者不小心删除的信息)进行恢复,保存退出;
重启系统后可以进入系统了(说明:vim操作文件方式有点麻烦可以百度搜索一下操作方式)。

作者:a29562268 发表于2017/11/23 21:42:44 原文链接
阅读:95 评论:0 查看评论

[TensorFlow学习手记] 3 - Variable变量和Placeholder简单运用

$
0
0

这里写图片描述


'''
Variable 变量
2017.11.23
TF中的变量必须先定义

'''

import tensorflow as tf 
state = tf.Variable(0,name='counter')   # 初始值0,名字为counter
print(state.name)

one = tf.constant(1)  # 常量1

new_value = tf.add(state , one)
update = tf.assign(state,new_value)

init = tf.initialize_all_variables()  # 定义变量必须初始化

with tf.Session() as sess:
    sess.run(init)
    for _ in range(3):
        sess.run(update)
        print(sess.run(state)) # 必须把state这个指针放进去


'''
placeholder
'''

input1 = tf.placeholder(tf.float32)
input2 = tf.placeholder(tf.float32)

output = tf.multiply(input1,input2)

with tf.Session() as sess:
    print(sess.run(output,feed_dict={input1:[7.],input2:[2.]}))  # placeholder 绑定 feed_dic 向 placeholder 内传值 ,字典形式{key:值}

作者:soulmeetliang 发表于2017/11/23 22:40:26 原文链接
阅读:32 评论:0 查看评论

Unity Shader 学习笔记(23) 运动模糊

$
0
0

Unity Shader 学习笔记(23) 运动模糊

参考书籍:《Unity Shader 入门精要》


运动模糊

两种常见方法:
- 积累缓存(accumulation buffer),混合连续多张图像。即需要同一帧里渲染多次场景,性能消耗较大。
- 速度缓存(velocity buffer),存储各个像素当前移动速度,利用该值判断模糊方向和大小,但曲线运动较大时会出现错误。


积累缓存

保持之前渲染结果不断叠加混合,模拟出运动轨迹(幻影)的视觉效果。即不用一帧里多次渲染。

MotionBlur类:

using UnityEngine;

public class MotionBlur : PostEffectsBase
{

    [Range(0.0f, 0.9f)]                         // 为1的时候完全代替当前帧的渲染结果
    public float blurAmount = 0.5f;             // 模糊参数

    private RenderTexture accumulationTexture;  // 保存之前图像的叠加效果

    void OnDisable()
    {
        DestroyImmediate(accumulationTexture);  // 用完就销毁,下一次开始应用这个重新叠加
    }

    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        if (TargetMaterial != null)
        {
            // 创建积累图像
            if (accumulationTexture == null || accumulationTexture.width != src.width || accumulationTexture.height != src.height)
            {
                DestroyImmediate(accumulationTexture);
                accumulationTexture = new RenderTexture(src.width, src.height, 0);
                accumulationTexture.hideFlags = HideFlags.HideAndDontSave;          // 变量不显示在Hierarchy中,也不会保存到场景
                Graphics.Blit(src, accumulationTexture);        // 原始图像存入积累纹理
            }

            // 表明需要进行一个恢复操作。渲染恢复操作:发生在渲染到纹理,而该纹理有没有被提前情况或销毁情况下。
            accumulationTexture.MarkRestoreExpected();          // accumulationTexture就不需要提前清空了

            TargetMaterial.SetFloat("_BlurAmount", 1.0f - blurAmount);

            // 混合当前屏幕和之前存的混合图像
            Graphics.Blit(src, accumulationTexture, TargetMaterial);
            // 最后输出混合图像
            Graphics.Blit(accumulationTexture, dest);
        }
        else
            Graphics.Blit(src, dest);
    }
}

Shader:

Properties {
    _MainTex ("Base (RGB)", 2D) = "white" {}
    _BlurAmount ("Blur Amount", Float) = 1.0
}
SubShader {
    CGINCLUDE

    ...

    // 更新RGB,当前图像。A通道设为模糊值,方便后面混合
    fixed4 fragRGB (v2f i) : SV_Target {
        return fixed4(tex2D(_MainTex, i.uv).rgb, _BlurAmount);
    }

    // 更新A,直接返回(保护纹理的A通道,不受混合时透明度影响)
    half4 fragA (v2f i) : SV_Target {
        return tex2D(_MainTex, i.uv);
    }

    ENDCG

    ZTest Always Cull Off ZWrite Off

    Pass {
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask RGB

        CGPROGRAM

        #pragma vertex vert  
        #pragma fragment fragRGB  

        ENDCG
    }

    Pass {   
        Blend One Zero
        ColorMask A

        CGPROGRAM  

        #pragma vertex vert  
        #pragma fragment fragA

        ENDCG
    }
}

速度缓存

两种方法:
- 把场景所有物体的速度渲染到一张纹理中。缺点是需要修改所有物体的Shader,计算速度并输出到一张纹理。
- 利用深度纹理计算每个像素世界空间下的位置。使用前一帧的变换矩阵计算,就可以得到前一帧该点的位置。通过这两个点就可以计算得到速度值。缺点是要在片元着色器中进行两次矩阵乘法。

使用深度纹理实现

MotionBlurWithDepthTexture类:

using UnityEngine;

// 使用深度纹理计算运动模糊
public class MotionBlurWithDepthTexture : PostEffectsBase
{
    [Range(0.0f, 1.0f)]
    public float blurSize = 0.5f;

    private Camera targetCamera;
    public Camera TargetCamera { get { return targetCamera = targetCamera == null ? GetComponent<Camera>() : targetCamera; } }

    private Matrix4x4 previousViewProjectionMatrix;         // 上一帧摄像机的 视角x投影 矩阵

    void OnEnable()
    {
        TargetCamera.depthTextureMode |= DepthTextureMode.Depth;  // 设置状态以获取摄像机的深度纹理
        previousViewProjectionMatrix = TargetCamera.projectionMatrix * TargetCamera.worldToCameraMatrix;
    }

    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        if (TargetMaterial != null)
        {
            TargetMaterial.SetFloat("_BlurSize", blurSize);

            // 上一帧的矩阵
            TargetMaterial.SetMatrix("_PreviousViewProjectionMatrix", previousViewProjectionMatrix);

            // 投影矩阵 * 视角矩阵 ,用于给下一帧计算该帧时的位置
            Matrix4x4 currentViewProjectionMatrix = TargetCamera.projectionMatrix * TargetCamera.worldToCameraMatrix;
            // 矩阵取逆,用于计算该帧的位置
            Matrix4x4 currentViewProjectionInverseMatrix = currentViewProjectionMatrix.inverse;
            TargetMaterial.SetMatrix("_CurrentViewProjectionInverseMatrix", currentViewProjectionInverseMatrix);
            previousViewProjectionMatrix = currentViewProjectionMatrix;
        }
        Graphics.Blit(src, dest, TargetMaterial);
    }
}

Shader:

Properties {
    _MainTex ("Base (RGB)", 2D) = "white" {}
    _BlurSize ("Blur Size", Float) = 1.0
}
SubShader {
    CGINCLUDE
    ...

    struct v2f {
        float4 pos : SV_POSITION;
        half2 uv : TEXCOORD0;
        half2 uv_depth : TEXCOORD1;
    };

    v2f vert(appdata_img v) {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);

        o.uv = v.texcoord;
        o.uv_depth = v.texcoord;

        #if UNITY_UV_STARTS_AT_TOP
        if (_MainTex_TexelSize.y < 0)
            o.uv_depth.y = 1 - o.uv_depth.y;
        #endif

        return o;
    }

    fixed4 frag(v2f i) : SV_Target {
        // 深度值。通过摄像机的深度纹理和纹理坐标计算(映射)出来
        float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth);
        // 构建像素的NDC坐标,xy像素的纹理坐标映射,
        float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1);
        // 当前帧的 视角x投影 矩阵的逆矩阵变换
        float4 D = mul(_CurrentViewProjectionInverseMatrix, H);
        // 并除w得到世界空间坐标 
        float4 worldPos = D / D.w;

        // 当前视角位置 
        float4 currentPos = H;

        // 上一帧位置
        float4 previousPos = mul(_PreviousViewProjectionMatrix, worldPos);
        previousPos /= previousPos.w;

        // 前一帧和当前帧位置差 求速度
        float2 velocity = (currentPos.xy - previousPos.xy)/2.0f;

        // 邻域像素采样,相加求平均
        float2 uv = i.uv;
        float4 c = tex2D(_MainTex, uv);
        uv += velocity * _BlurSize;
        for (int it = 1; it < 3; it++, uv += velocity * _BlurSize) {
            float4 currentColor = tex2D(_MainTex, uv);
            c += currentColor;
        }
        c /= 3;

        return fixed4(c.rgb, 1.0);
    }

    ENDCG

    Pass {      
        ZTest Always Cull Off ZWrite Off

        CGPROGRAM  

        #pragma vertex vert  
        #pragma fragment frag  

        ENDCG  
    }
} 
作者:l773575310 发表于2017/11/23 22:49:14 原文链接
阅读:43 评论:0 查看评论

Java并发编程札记-(二)JUC概述

$
0
0

从今天开始学习JUC。JUC是java.util.concurrent包的简称。下图是JUC的整体结构。

MarkdownPhotos/master/CSDNBlogs/concurrency/0201/J.U.C.png

atomic

以下是JUC中的原子类。

MarkdownPhotos/master/CSDNBlogs/concurrency/0201/atomic.png

locks

以下是JUC中的锁,也称显示锁。

MarkdownPhotos/master/CSDNBlogs/concurrency/0201/locks.png

collections

以下是JUC中的集合。

MarkdownPhotos/master/CSDNBlogs/concurrency/0201/collections.png

executor

以下是JUC中与线程池有关的类。

MarkdownPhotos/master/CSDNBlogs/concurrency/0201/executor.png

tools

以下是JUC中的工具类。

MarkdownPhotos/master/CSDNBlogs/concurrency/0201/tools.png

本文就讲到这里,想了解Java并发编程更多内容请参考:

END.

作者:panweiwei1994 发表于2017/11/23 23:46:21 原文链接
阅读:55 评论:0 查看评论

Eric6与pyqt5学习笔记 6【实战2 window伪文本编辑器】【纯eric6操作】

$
0
0

从知乎上学习了一波eric的操作,果然很爽,比之前纯代码开发确实舒服很多,不过推荐新手还是了解一点代码的含义~要不然eric需要个人进行修改的地方你会读不懂生成的代码的意思~
这里补充一波pyqt5基本常用类:

  • QtCore模块涵盖了包的核心的非GUI功能,此模块被用于处理程序中涉及到的
    time、文件、目录、数据类型、文本流、链接、mime、线程或进程等对象。

  • QtWidgets模块包含了一整套UI元素组件,用于建立符合系统风格的classic界面,非常方便,可以在安装时选择是否使用此功能。

  • QtMultimedia模块包含了一套类库,该类库被用于处理多媒体事件,通过调用API接口访问摄像头、语音设备、收发消息(radio functionality)等。

  • QtNetwork模块包含用于网络编程的类库,这组类程序通过提供便捷的TCP/IP 及 UDP 的 c/s程式码集合,使得基于Qt的网络编程更容易。

  • QtPositioning模块用于获取位置信息,此模块允许使用多种方式达成定位,包括但不限于:卫星、无线网、文字信息。此应用一般用于网络地图定位系统。

  • Enginio模块用于构建客户端的应用程式库,用于在运行时访问 Qt Cloud 服务器托管的应用程序。

  • QtWebSockets模块包含了一组类程序,用以实现websocket协议。

  • QtWebKit包含了用于实现基于webkit2的网络浏览器的类库。

  • QtWebKitWidgets模块包含用于基于WebKit1的Web浏览器实现的类,用于基于QtWidgets的应用程序

  • QtXml模块包含了用于处理XML的类库,此模块为SAX和DOM API 的实现提供了方法。

  • QtSvg模块通过一组类,为显示矢量图形文件的内容提供了方法。

  • QtSql模块提供了数据库对象的接口以供使用

  • QtTest模块包含了可以通过单元测试,以调试PyQt5应用程式的功能。

    这次带来的实战是纯eric6开发的一个伪windows文本编辑器
    效果图如下:
    这里写图片描述
    windows文本编辑器
    这里写图片描述
    这次由于是纯eric6制作,
    之后录个短视频吧,再补发,
    注意eric制作时的一个点,二级菜单不能直接命名中文,可先命名英文,然后再改
    老样子,代码放github了,有兴趣的可以看下

作者:wy_97 发表于2017/11/24 0:51:51 原文链接
阅读:51 评论:0 查看评论

[TensorFlow]学习手记 4 - 激励函数

$
0
0

这里写图片描述

Result

这里写图片描述


Code

import tensorflow as tf 
import numpy as np 
import matplotlib.pyplot as plt 

# fake data
x = np.linspace(-5,5,200)   # x data,shape(100,1)

# following are popular activation function
y_relu = tf.nn.relu(x)
y_sigmoid = tf.nn.sigmoid(x)
y_tanh = tf.nn.tanh(x)
y_softplus = tf.nn.softplus(x)
y_softmax = tf.nn.softmax(x) # softmax is a special kind of activation function, it is about probability

sess = tf.Session()
y_relu,y_sigmoid,y_tanh,y_softplus,y_sigmoid = sess.run([y_relu,y_sigmoid,y_tanh,y_softplus,y_sigmoid])

# plt to visualize these activation function
plt.figure(1,figsize=(8,6))  # 自定义画布大小
plt.subplot(221) 
'''
将figure设置的画布大小分成几个部分,参数‘221’表示2(row)x2(colu),即将画布分成2x2,
两行两列的4块区域,1表示选择图形输出的区域在第一块,图形输出区域参数必须在“行x列”范围,
此处必须在1和2之间选择——如果参数设置为subplot(111),则表示画布整个输出,不分割成小块区域,
图形直接输出在整块画布上
'''
plt.plot(x,y_relu,c='red',label='relu') #画点
plt.ylim((-1,5)) # 设置y轴范围
plt.legend(loc='best') 
'''
'best'         : 0, (only implemented for axes legends)(自适应方式)
'upper right'  : 1,
'upper left'   : 2,
'lower left'   : 3,
'lower right'  : 4,
'right'        : 5,
'center left'  : 6,
'center right' : 7,
'lower center' : 8,
'upper center' : 9,
'center'       : 10,
'''

plt.subplot(222)
plt.plot(x,y_sigmoid,c='red',label='sigmoid')
plt.ylim((-0.2,1.2))
plt.legend(loc='best')

plt.subplot(223)
plt.plot(x,y_relu,c='red',label='relu')
plt.ylim((-1,5))
plt.legend(loc='best')

plt.subplot(224)
plt.plot(x,y_tanh,c='red',label='tanh')
plt.ylim((-1.2,1.2))
plt.legend(loc='best')

plt.show()
作者:soulmeetliang 发表于2017/11/24 9:20:07 原文链接
阅读:25 评论:0 查看评论

Android官方ORM数据库Room技术解决方案:@Embedded内嵌对象(二)

$
0
0
Android官方ORM数据库Room技术解决方案:@Embedded内嵌对象(二)


(一)附录1简介了Android Room的基本使用。在附录1例子中,User对象元素均为普通的Java基本数据类型,但是实际的开发中,通常建立的持久化存储对象复杂,且通常是结构化的Java对象,互相之间存在引用或者内嵌关系。

Android Room支持数据库表Java对象通过注解符@Embedded内嵌一个Java对象。这样就像过去的ORM数据库一样,比如构造一个名为Info的Java对象,作为一个成员变量添加到User里面:

package zhangphil.demo;

import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Embedded;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.PrimaryKey;

/**
 * Created by Phil on 2017/11/22.
 */

@Entity(tableName = "user_table")
public class User {
    @PrimaryKey(autoGenerate = true)
    public int userId;

    @ColumnInfo(name = "userName")
    public String name;

    @ColumnInfo(name = "userAge")
    public int age;

    @ColumnInfo(name = "updateTime")
    public long updateTime;

    @Embedded
    public Info info;
}

(二)Info对象本身也是一个Android Room的@Entity。也有自己的列名和主键等完整的Android Room数据表要素,Info.java:
package zhangphil.demo;

import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.PrimaryKey;

/**
 * Created by Phil on 2017/11/23.
 */

@Entity(tableName = "info_table")
public class Info {
    @PrimaryKey(autoGenerate = true)
    public int infoId;

    @ColumnInfo(name = "blog")
    public String blog;

    @ColumnInfo(name = "content")
    public String content;
}

(三)需要注意的是,通过Android Room的@Embedded符号内嵌的数据表,列表名将自动二次添加到“宿主”对象中。本例是User钟内嵌了Info,那么User数据库表user_table中将会被Android Room自动添加Info里面的列名字段。User的user_table中原有userId,name,age,updateTime四列,由于@Embedded了Info,那么Info里面的blog,content将会自动添加到User数据表中。但是Info的主键infoId将在User中被忽略不再被作为主键。
 
(四)写一个MainActivity.java测试:
package zhangphil.demo;

import android.arch.persistence.room.Room;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private String TAG = "输出";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        new Thread(new Runnable() {
            @Override
            public void run() {
                databaseOperation();
            }
        }).start();
    }

    private void databaseOperation() {
        UserDatabase mUserDatabase = Room.databaseBuilder(getApplicationContext(), UserDatabase.class, "users").build();
        UserDao mUserDao = mUserDatabase.getUserDao();

        //写数据库
        writeUserDatabase(mUserDao, "zhangphil", 18);
        readDatabase(mUserDao);

        //关闭数据库
        mUserDatabase.close();
    }

    private void readDatabase(UserDao dao) {
        Log.d(TAG, "读数据库...");
        List<User> users = dao.getAllUsers();
        for (User u : users) {
            Log.d(TAG, u.userId + "," + u.name + "," + u.age + ","+u.info.blog+","+u.info.content);
        }
        Log.d(TAG, "读数据库完毕.");
    }

    private void writeUserDatabase(UserDao dao, String name, int age) {
        Info info = new Info();
        info.blog = "http://blog.csdn.net/zhangphil";
        info.content = "Android";

        User user = new User();
        user.name = name;
        user.age = age;
        user.updateTime = System.currentTimeMillis();

        user.info = info;

        dao.insertUser(user);
    }
}


代码运行结果,logcat输出:

11-24 09:20:00.716 30805-30851/zhangphil.demo D/输出: 读数据库...
11-24 09:20:00.723 30805-30851/zhangphil.demo D/输出: 1,zhangphil,18,http://blog.csdn.net/zhangphil,Android
11-24 09:20:00.723 30805-30851/zhangphil.demo D/输出: 读数据库完毕.



附录:
1,《Android官方ORM数据库Room技术解决方案简介(一)》链接:http://blog.csdn.net/zhangphil/article/details/78611632 
2,《Android ORMLite数据库简介》链接:http://blog.csdn.net/zhangphil/article/details/46878075 
3,《Android ORMLite ForeignCollection关联外部集合》链接:http://blog.csdn.net/zhangphil/article/details/46891021 
作者:zhangphil 发表于2017/11/24 9:21:50 原文链接
阅读:53 评论:0 查看评论
Viewing all 35570 articles
Browse latest View live


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