首页 文章 精选 留言 我的

精选列表

搜索[知识管理],共10000篇文章
优秀的个人博客,低调大师

年薪50w的BATAndroid架构师知识体系详解

前言 近几年,Android 开发的套路日趋成熟,越来越多的 Android 工程师获得了「高级」的称号,也有不少人在参与公司的 App 从无到有再到火爆的整个开发过程中,顺理成章地拿到了 Leader 职位。 但对于自己的水平,多数人却并不满意,甚至有不少人觉得自己的水平被同事和老板「高估」了。 市场真正需要的所谓「高级架构师」到底需要具备什么条件?在此和大家探讨一下 Android 工程师在当下这个时代该如何真正的成为高级架构师 目录 1.Android初中级工程师遇到了哪些瓶颈? 2.什么是Android架构师 3.最主流的Android架构师技术体系详解 4.Android高级架构技术;如何系统化学习? 5Android架构师的门槛,免费资料分享? 内容 1.Android初中级工程师遇到了哪些瓶颈? 2.什么是Android架构师? 笼统的说,比

优秀的个人博客,低调大师

阿里云大数据ACP认证知识点梳理7——MAP REDUCE

1、输入数据:对文本进行分片,将每片内的数据作为单个Map Worker的输入。 2、Map阶段:Map处理输入,每获取一个数字,将数字的Count 设置为1,并将此对输出,此时以Word作为输出数据的Key。 3、Shuffle>合并排序:在Shuffle阶段前期,首先对每个Map Worker的输出,按照Key值(即Word值)进行排序。排序后进行Combiner操作,即将Key值(Word值)相同的Count累加,构成一个新的对。此过程被称为合并排序。4、Shuffle>分配Reduce:在Shuffle阶段后期,数据被发送到Reduce端。Reduce Worker收到数据后依赖Key值再次对数据排序。 5、Reduce阶段:每个Reduce Worker对数据进行处理时,采用与Combiner相同的逻辑,将Key值

优秀的个人博客,低调大师

安卓中高级开发面试知识点之——缓存

前言 几乎所有的项目都做了缓存,但是缓存做的怎么样,其实只有我们自己知道。缓存做的好,没有网络也能流畅的使用;再多的数据请求都不会出现卡顿延迟等待很久的情况。 程序中除了图片缓存(三级缓存),还有信息缓存。当用户无法联网时,app会默认显示缓存的数据。 前言缓存方式 SQLite 下载完数据文件后,把文件的相关信息如url,路经,下载时间,过期时间等存放到数据库,把url作为唯一的标识。下次下载的时候根据url先从数据库中查询,如果查询到当前时间并未过期,就根据路径读取本地文件,从而实现缓存的效果。 文件缓存使用File.lastModified()方法得到文件的最后修改时间,与当前时间判断是否过期,从而实现缓存效果。数据格式为JSON。 缓存方式两点说明 1、不同类型的文件的缓存时间不一样。笼统的说,不变文件的缓存时间是永久,变化文件的缓存时间是最大忍受不变时间。说白点,图片文件内容是不变的,一般存在SD卡上直到被清理,我们是可以永远读取缓存的。配置文件内容是可能更新的,需要设置一个可接受的缓存时间。 2、不同环境下的缓存时间标准不一样。无网络环境下,我们只能读取缓存文件,为了应用有东西显示,没有什么过期之说了。 WiFi 网络环境下,缓存时间可以设置短一点,一是网速较快,而是流量不要钱。 3G 流量环境下,缓存时间可以设置长一点,节省流量,而且用户体验也更好。 缓存时间 app中多个页面的缓存时间是不一样的,对实时性要求高的页面缓存时间较短。而http消息头中包含有缓存时间,android端无需自己记录/规定缓存时间,读取即可。 http协议对缓存的支持 Expires & Cache-Control Expires响应首部给出了响应失效的绝对时间,这样客户端就可以缓存一份副本,在这个时间到期之前, 不用去询问服务器它是否有效了。http1.0引入。 例:Expires: Thu, 03 Oct 1997 17:15:00 GMT Cache-Control首部用于传输对象的缓存信息。http1.1引入。它的值是一个缓存指令,给出了与某个对象可缓存性有关的特有指令。这个首部可以出现在请求或者响应头中。例如:Cache-Control: no-cache CacheControl 有两个字段表达响应的过期时间:max-age和max-stale 前者表示:max-age秒内,网页再有请求,你不要来我服务端,直接取你本地缓存的结果好了 后者表示:max-stale秒内的请求,你可以使用本地缓存的,但还是要来我服务端问问,到底行不行,当然,这里要带上Last Modified等信息 ,如果服务端返回了304,那说明你本地缓存继续用吧,我不给你响应体200的话,自然就带上了响应体。 Expires和Cache-Control作用一致,都是指当前资源的有效期,控制是直接从缓存获取数据还是重新发送请求到服务器取数据。 缓存算法 1、LRU - 最近最少使用(最后访问时间)替换掉最近被请求最少的文档。这一传统策略在实际中应用最广。在CPU缓存淘汰和虚拟内存系统中效果较好。 2、LRU-K LRU-K 中的K代表最近使用的次数,也可以认为是LRU-1。LRU-K的主要目的是为了解决LRU算法“缓存污染”的问题,其核心思想是将“最近使用过1次”的判断标准扩展为“最近使用过K次”。相比LRU,LRU-K需要多维护一个队列,用于记录所有缓存数据被访问的历史。只有当数据的访问次数达到K次的时候,才将数据放入缓存。当需要淘汰数据时,LRU-K会淘汰第K次访问时间距当前时间最大的数据。如下: 3.LFU - 最不经常使用(访问次数)替换掉访问次数最少的。这一策略意图保留最常用的、最流行的对象,替换掉很少使用的那些。 LFU 的每个数据块都有一个引用计数,所有数据块按照引用计数排序,具有相同引用计数的数据块则按照时间排序。如下: 4.SIZE(缓存大小)替换size最大的对象。这一策略通过淘汰一个大对象而不是多个小对象来提高命中率。不过,可能有些进入缓存的小对象永远不会再被访问。SIZE策略没有提供淘汰这类对象的机制,会导致“缓存污染”(大量偶发性的数据访问让内存中存放大量冷数据,也即是缓存污染)。 引申几个问题,面试常被问到的问题: 1、http的缓存是怎么做的 ? 2、用的什么?(这个问题和线程会同问,一般问一个。) 答案请自行百度。我就不说了。因为我看过http的源码,看过他的缓存和线程。是自定义的。大家做一个了解就行。 这个问题被问到的频率不高。而高频问的一般是这种问题:有一个网络请求,有很多数据(比如一年,两年,每天的数据都要请求出来),然后拿到数据后做处理,然后recycleview(或listview)展示出来。像这种请求由于数据很多, 会有一段时间的等待,导致页面UI数据延迟的情况的解决方案。大家心里要做一个准备。 欢迎加入Android开发技术交流QQ群;701740775 本群提供Android高级开发资料、高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)等相关资料和解答 不懂得问题都可以在本群提出来 还会有职业生涯规划以及面试指导 进群修改群备注:开发年限-地区-经验 方便架构师解答问题 ---------------------

优秀的个人博客,低调大师

多线程并发相关的几个重要基础知识点解析

问:volatile 变量和 atomic 变量有什么不同? 答:volatile 变量和 atomic 变量看起来很像,但功能却不一样。 volatile 变量可以确保先行关系,即写操作会发生在后续的读操作之前, 但它并不能保证原子性,也就是说其可以保证变量的可见性与有序性,无法保证原子性。例如用 volatile 修饰 count 变量,则 count++ 操作就不是原子性的。 AtomicInteger 类提供的 atomic 方法可以让这种操作具有原子性,其保证了并发安全的可见性、有序性、原子性三要素。如 getAndIncrement() 方法会原子性的进行增量操作把当前值加一,其它数据类型和引用变量也可以进行相似操作。 问:如果你提交任务时线程池队列已满了则会发会生什么? 答:这取决于线程池队列的实现。 如果线程池使用的是 LinkedBlockingQueue 无界队列,则继续添加任务到阻塞队列中等待执行理论上是不会满的,因为 LinkedBlockingQueue 可以近乎认为是一个无穷大的队列,可以无限存放任务。 如果线程池使用的是有界队列(比如 ArrayBlockingQueue)的话,任务首先会被添加到队列中,当队列满了则会使用拒绝策略 RejectedExecutionHandler 处理满了的任务,默认是 AbortPolicy,我们可以在创建线程池时修改这个策略,譬如是静默忽略还是崩溃等。 问:简单说说并发饥饿与死锁的区别? 答:饥饿是指系统不能保证某个进程的等待时间上界,从而使该进程长时间等待,当等待时间给进程推进和响应带来明显影响时,称发生了进程饥饿。当饥饿到一定程度的进程所赋予的任务即使完成也不再具有实际意义时称该进程被饿死。 死锁是指在多道程序系统中,一组进程中的每一个进程都无限期等待被该组进程中的另一个进程所占有且永远不会释放的资源。 他们的相同点是二者都由于竞争资源而引起。 他们的不同点可以从几个方面来总结: 从进程状态考虑,死锁进程都处于等待状态,忙等待(处于运行或就绪状态)的进程并非处于等待状态,但却可能被饿死; 死锁进程等待永远不会被释放的资源,饿死进程等待会被释放但却不会分配给自己的资源,表现为等待时限没有上界(排队等待或忙式等待); 死锁一定发生了循环等待,而饿死则不然。这也表明通过资源分配图可以检测死锁存在与否,但却不能检测是否有进程饿死; 死锁一定涉及多个进程,而饥饿或被饿死的进程可能只有一个; 在饥饿的情形下,系统中有至少一个进程能正常运行,只是饥饿进程得不到执行机会,而死锁则可能会最终使整个系统陷入死锁并崩溃; 欢迎工作一到五年的Java工程师朋友们加入Java架构开发:744677563 本群提供免费的学习指导 架构资料 以及免费的解答 不懂得问题都可以在本群提出来 之后还会有职业生涯规划以及面试指导

优秀的个人博客,低调大师

Scrapy框架-去重原理讲解、数据收集以及信号量知识

scrapy的去重原理 信号无处不在 【知其然且知其所以然,才能够更好的理解这个框架,而且在使用和改动的时候也能够想出更合理的方法。】 (开始测试前,到settings.py中注释掉下载中间件的设置,这里用jobbole爬虫来测试,所以之前写的调用chrome的两个方法init和spider_closed都要注释掉。) 这里你们可以用自己的爬虫来测试,不一定要按我的来测试。 到scrapy源码包 [项目\Lib\site-packages\scrapy\dupefilters.py] 里面找去重的代码,RFPDupeFilter类就是去重器,里面有个方法叫做request_seen,它在scheduler(发起请求的第一时间)的时候被调用。它代码里面调用了request_fingerprint方法(就是给request生成一个指纹),连续两次跟进代码就进入到了request.py文件的request_fingerprint方法中,方法中有一句代码: fp = hashlib.sha1() … … cache[include_headers] = fp.hexdigest() 就是给每一个传递过来的url生成一个固定长度的唯一的哈希值。这种量级千万到亿的级别内存是可以应付的。 然后看到init方法: def __init__(self, path=None, debug=False): self.file = None self.fingerprints = set() self.logdupes = True self.debug = debug self.logger = logging.getLogger(__name__) if path: self.file = open(os.path.join(path, 'requests.seen'), 'a+') self.file.seek(0) self.fingerprints.update(x.rstrip() for x in self.file) 里面有一句代码 self.fingerprints = set(),就是通过set集合的特点(set不允许有重复值)进行去重。 可以用断点调试的方法进行跟踪查看。 Telnet Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序,用它连接到服务器。终端使用者可以在telnet程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。要开始一个telnet会话,必须输入用户名和密码来登录服务器。Telnet是常用的远程控制Web服务器的方法。 可以用这个在本地操控远程的scrapy,telnet是默认开启的,当scrapy运行的时候,会自动开启端口,运行框会有显示: [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023 我们可以通过cmd连接测试。 要测试,就要开启windows电脑的telnet功能。在控制面板-程序与功能-启用或关闭windows功能,找到Telnet服务和客户端,打上勾即可。 linux系统也是默认没有Telnet的,需要安装,比如我的Deepin系统就需要用命令安装: sudo apt-get install telnet 先运行scrap有,然后在cmd(linux在终端输入)中输入: telnet localhost 6023 连接成功后会显示: >>> 符号,我们输入est()可以查看当前scrapy的运行状态等属性。官方文档有Telnet的相关介绍,里面包括有一些可用的变量/查看引擎状态/暂停,恢复和停止scrapy/终端信号/设置等内容。 Telnet的源码在site-package/scrapy/extensions目录下的telnet.py文件中。 数据收集器 官方文档中有对数据收集器的介绍. 数据收集器可以应用在很多地方,举例子:如果你想知道scrapy总共发出了多少个request请求;或者你想记录总共发起了多少次yeild,都可以用数据收集器记录,它不用打开,默认可以直接使用。 在jobbole爬虫的JobboleSpider类里面新增代码: # 收集jobbole.com所有的500、404页面及页面数量 handle_httpstatus_list = [500,404] def __init__(self): self.fail_urls = [] 然后到parse方法中新增代码: if response.status == 500 or response.status == 404: self.fail_urls.append(response.url) self.crawler.stats.inc_value("failed_url") 就可以实现对页面数量和页面url的收集,可以自定义保存. 信号 scrapy的中间件与通信都是通过信号来传递的,官网有文档 可以在您的Scrapy项目中捕捉一些信号(使用 extension)来完成额外的工作或添加额外的功能,扩展Scrapy。 信号提供了一些参数,不过处理函数不用接收所有的参数 - 信号分发机制(singal dispatching mechanism)仅仅提供处理器(handler)接受的参数。 代码演示: from scrapy.xlib.pydispatch import dispatcher from scrapy import signals def parse(self, response): """ 正式进入爬取区域 """ dispatcher.connect(self.handler_spider_closed, signals.spider_closed) def handler_spider_closed(self, spider, reason): print("这个名为:" + spider.name + "的爬虫已经关闭了,原因是:" + reason) 得到的输出结果是在爬虫关闭后: 'start_time': datetime.datetime(2018, 1, 20, 3, 5, 49, 791339)} 2018-01-20 11:05:51 [scrapy.core.engine] INFO: Spider closed (finished) 这个名为:dongmeng的爬虫已经关闭了,原因是:finished dispatcher.connect监听爬虫signals(信号),当收到爬虫关闭(signals.spider_closed)的信号时,调用handler_spider_closed方法。而handler_spider_closed我只是简单的编写了一个关闭的原因而已,还可以做更深入的操作。 由此可以看出,爬虫的信号监听和状态操作可以做很多的事情,比如打开爬虫时、爬虫空闲时可以收集当前request请求队列、记录404页面数量或者200状态的页面有那些、多少条等。 扩展 官方文档对扩展有介绍: 扩展框架提供一个机制,使得你能将自定义功能绑定到Scrapy。 扩展只是正常的类,它们在Scrapy启动时被实例化、初始化。 scrapy里面的中间件都是一种扩展。 为了更好的理解扩展,这里用源码跟踪的形式来理解,到[项目/Lib/site-packages/scrapy/extensions]目录下找到corestats.py文件。 from_crawler方法里面记录了很多的信号量: @classmethod def from_crawler(cls, crawler): o = cls(crawler.stats) crawler.signals.connect(o.spider_opened, signal=signals.spider_opened) crawler.signals.connect(o.spider_closed, signal=signals.spider_closed) crawler.signals.connect(o.item_scraped, signal=signals.item_scraped) crawler.signals.connect(o.item_dropped, signal=signals.item_dropped) crawler.signals.connect(o.response_received, signal=signals.response_received) return o 下面对应都写着具体的执行方法。比如spider_opened爬虫启动的时候就会: def spider_opened(self, spider): self.stats.set_value('start_time', datetime.datetime.utcnow(), spider=spider) 记录爬虫启动的时间。 还有spider_closed关闭爬虫的时候就会: def spider_closed(self, spider, reason): self.stats.set_value('finish_time', datetime.datetime.utcnow(), spider=spider) self.stats.set_value('finish_reason', reason, spider=spider) 记录下关闭时间和关闭的原因等。 到[项目/Lib/site-packages/scrapy/extensions]memusage.py文件是监控内存使用的。里面有一串代码: crawler.signals.connect(self.engine_started, signal=signals.engine_started) crawler.signals.connect(self.engine_stopped, signal=signals.engine_stopped) 是主要的绑定项 比如这个engine_started方法开始就记录内存使用信息。 self.crawler = crawler self.warned = False self.notify_mails = crawler.settings.getlist('MEMUSAGE_NOTIFY_MAIL') self.limit = crawler.settings.getint('MEMUSAGE_LIMIT_MB')*1024*1024 self.warning = crawler.settings.getint('MEMUSAGE_WARNING_MB')*1024*1024 self.check_interval = crawler.settings.getfloat('MEMUSAGE_CHECK_INTERVAL_SECONDS') self.mail = MailSender.from_settings(crawler.settings) 里面的大概意思就是设置了监控内存的定时时间/不同状态的操作等,可以在engine_started里面加逻辑,想对内存干什么就干什么。

优秀的个人博客,低调大师

Mybatis【一对多、多对一、多对多】知识要点

Mybatis【多表连接】 我们在学习Hibernate的时候,如果表涉及到两张的话,那么我们是在映射文件中使用<set>..<many-to-one>等标签将其的映射属性关联起来的...那么在我们Mybatis中又怎么做呢??? 先来回顾一下我们SQL99的语法: 一)内连接(等值连接):查询客户姓名,订单编号,订单价格 --------------------------------------------------- select c.name,o.isbn,o.price from customers c inner join orders o where c.id = o.customers_id; --------------------------------------------------- select c.name,o.isbn,o.price from customers c join orders o where c.id = o.customers_id; --------------------------------------------------- select c.name,o.isbn,o.price from customers c,orders o where c.id = o.customers_id; --------------------------------------------------- select c.name,o.isbn,o.price from customers c join orders o on c.id = o.customers_id; --------------------------------------------------- 注意:内连接(等值连接)只能查询出多张表中,连接字段相同的记录 二)外连接:按客户分组,查询每个客户的姓名和订单数 --------------------------------------------------- 左外连接: select c.name,count(o.isbn) from customers c left outer join orders o on c.id = o.customers_id group by c.name; --------------------------------------------------- 右外连接: select c.name,count(o.isbn) from orders o right outer join customers c on c.id = o.customers_id group by c.name; --------------------------------------------------- 注意:外连接既能查询出多张表中,连接字段相同的记录;又能根据一方,将另一方不符合相同记录强行查询出来 三)自连接:求出AA的老板是EE --------------------------------------------------- 内自连接: select users.ename,boss.ename from emps users inner join emps boss on users.mgr = boss.empno; --------------------------------------------------- 外自连接: select users.ename,boss.ename from emps users left outer join emps boss on users.mgr = boss.empno; --------------------------------------------------- 注意:自连接是将一张表,通过别名的方式,看作多张表后,再进行连接。 这时的连接即可以采用内连接,又可以采用外连接 由于我们Mybatis中并没有像Hibernate这样全自动化的,因此我们是没有<set>..<many-to-one>等标签的,我们还是使用手写SQL语句来使我们的关联属性连接起来... 一对一 需求: 学生和身份证 设计表: --mysql create table cards( cid int(5) primary key, cnum varchar(10) ); create table students( sid int(5) primary key, sname varchar(10), scid int(5), constraint scid_fk foreign key(scid) references cards(cid) ); insert into cards(cid,cnum) values(1,'111'); insert into students(sid,sname,scid) values(1,'哈哈',1); select * from cards; select * from students; 实体 /** * 身份证(单方) * @author AdminTC */ public class Card { private Integer id; private String num; public Card(){} public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getNum() { return num; } public void setNum(String num) { this.num = num; } } /** * 学生(单方) * @author AdminTC */ public class Student { private Integer id; private String name; private Card card;//关联属性 public Student(){} public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Card getCard() { return card; } public void setCard(Card card) { this.card = card; } } 映射文件 由于我们有两个实体,因此我们会有两个映射文件 Student映射文件 <?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="studentNamespace"> <resultMap type="zhongfucheng2.Student" id="studentMap"> <id property="id" column="sid"/> <result property="name" column="sname"/> </resultMap> </mapper> Card映射文件 <?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="cardNamespace"> <resultMap type="zhongfucheng2.Card" id="cardMap"> <id property="id" column="cid"/> <result property="num" column="cnum"/> </resultMap> </mapper> DAO层 现在我想根据学生的编号查询学生的信息和身份证信息! 由于该查询着重是查询学生的信息,于是我们在学生的映射文件中写SQL语句 按照需求,我们写出来的SQL语句是这样子的。 select * from zhongfucheng.students s,zhongfucheng.cards c where c.cid = s.scid and sid=1; 我来看一下查询结果: 我们的实体与映射表中,Student实体是没有关联其他的字段的,仅仅是写出了该实体的自带的属性。 <resultMap type="zhongfucheng2.Student" id="studentMap"> <id property="id" column="sid"/> <result property="name" column="sname"/> </resultMap> 明显地,我们Student是不能封装返回的结果,因此我们需要将关联属性进行关联起来! <resultMap type="zhongfucheng2.Student" id="studentMap"> <id property="id" column="sid"/> <result property="name" column="sname"/> <!-- property写的是在Student实体中写关联字段的属性变量名称 resultMap写的是映射文件中的命名空间.id --> <association property="card" resultMap="cardNamespace.cardMap"/> </resultMap> 我们关联了以后,Student实体就能够封装返回的结果了 <resultMap type="zhongfucheng2.Student" id="studentMap"> <id property="id" column="sid"/> <result property="name" column="sname"/> <!-- property写的是在Student实体中写关联字段的属性变量名称 resultMap写的是映射文件中的命名空间.id --> <association property="card" resultMap="cardNamespace.cardMap"/> </resultMap> <select id="findById" parameterType="int" resultMap="studentMap"> select * from zhongfucheng.students s,zhongfucheng.cards c where c.cid = s.scid and sid=#{id}; </select> 查询编号为1的学生信息【包括身份证编号】 public Student findById(int id) throws Exception { //得到连接对象 SqlSession sqlSession = MybatisUtil.getSqlSession(); try{ return sqlSession.selectOne("studentNamespace.findById", id); /* sqlSession.commit();*/ }catch(Exception e){ e.printStackTrace(); sqlSession.rollback(); throw e; }finally{ MybatisUtil.closeSqlSession(); } } public static void main(String[] args) throws Exception { StudentDao studentDao = new StudentDao(); Student student = studentDao.findById(1); System.out.println(student.getId() + "----" + student.getName() + "----" + student.getCard().getNum()); } 一对多 需求: 一个班级有多个学生,查询java学科有哪些学生信息 设计数据库表 create table grades( gid int(5) primary key, gname varchar(10) ); create table students( sid int(5) primary key, sname varchar(10), sgid int(5), constraint sgid_fk foreign key(sgid) references grades(gid) ); insert into grades(gid,gname) values(1,'java'); insert into students(sid,sname,sgid) values(1,'哈哈',1); insert into students(sid,sname,sgid) values(2,'呵呵',1); select * from grades; select * from students; 实体 package zhongfucheng2; import java.util.ArrayList; import java.util.List; /** * 学科(单方) * @author AdminTC */ public class Grade { private Integer id; private String name; private List<Student> studentList = new ArrayList<Student>();//关联属性 public Grade(){} public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Student> getStudentList() { return studentList; } public void setStudentList(List<Student> studentList) { this.studentList = studentList; } } package zhongfucheng2; /** * 学生(多方) * @author AdminTC */ public class Student { private Integer id; private String name; private Grade grade;//关联属性 public Student(){} public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Grade getGrade() { return grade; } public void setGrade(Grade grade) { this.grade = grade; } } 映射文件SQL语句 <mapper namespace="studentNamespace"> <resultMap type="zhongfucheng2.Student" id="studentMap"> <id property="id" column="sid"/> <result property="name" column="sname"/> </resultMap> <!--查询选修的java学科有多少位学生--> <!--由于我们只要查询学生的名字,而我们的实体studentMap可以封装学生的名字,那么我们返回studentMap即可,并不需要再关联到学科表--> <select id="findByGrade" parameterType="string" resultMap="studentMap"> select s.sname,s.sid from zhongfucheng.students s,zhongfucheng.grades g WHERE s.sgid=g.gid and g.gname=#{name}; </select> </mapper> <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="gradeNamespace"> <resultMap type="zhongfucheng2.Grade" id="gradeMap"> <id property="id" column="gid"/> <result property="name" column="gname"/> </resultMap> </mapper> DAO public List<Student> findByGrade(String grade) throws Exception { //得到连接对象 SqlSession sqlSession = MybatisUtil.getSqlSession(); try{ return sqlSession.selectList("studentNamespace.findByGrade", grade); /* sqlSession.commit();*/ }catch(Exception e){ e.printStackTrace(); sqlSession.rollback(); throw e; }finally{ MybatisUtil.closeSqlSession(); } } public static void main(String[] args) throws Exception { StudentDao studentDao = new StudentDao(); List<Student> student = studentDao.findByGrade("java"); for (Student student1 : student) { System.out.println(student1.getName()); } } 多对多 需求: 学生和课程 数据库表 create table students( sid int(5) primary key, sname varchar(10) ); create table courses( cid int(5) primary key, cname varchar(10) ); create table middles( msid int(5), mcid int(5), primary key(msid,mcid) ); insert into students(sid,sname) values(1,'哈哈'); insert into students(sid,sname) values(2,'呵呵'); insert into courses(cid,cname) values(1,'java'); insert into courses(cid,cname) values(2,'android'); insert into middles(msid,mcid) values(1,1); insert into middles(msid,mcid) values(1,2); insert into middles(msid,mcid) values(2,1); insert into middles(msid,mcid) values(2,2); select * from students; select * from courses; select * from middles; 实体 package cn.itcast.javaee.mybatis.many2many; import java.util.ArrayList; import java.util.List; /** * 课程(多方) * @author AdminTC */ public class Course { private Integer id; private String name; private List<Student> studentList = new ArrayList<Student>();//关联属性 public Course(){} public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Student> getStudentList() { return studentList; } public void setStudentList(List<Student> studentList) { this.studentList = studentList; } } package cn.itcast.javaee.mybatis.many2many; import java.util.ArrayList; import java.util.List; /** * 学生(多方) * @author AdminTC */ public class Student { private Integer id; private String name; private List<Course> courseList = new ArrayList<Course>();//关联属性 public Student(){} public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Course> getCourseList() { return courseList; } public void setCourseList(List<Course> courseList) { this.courseList = courseList; } } 映射文件 <?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="courseNamespace"> <resultMap type="cn.itcast.javaee.mybatis.many2many.Course" id="courseMap"> <id property="id" column="cid"/> <result property="name" column="cname"/> </resultMap> <!-- 查询哈哈选学了哪些课程 --> <select id="findAllByName" parameterType="string" resultMap="courseMap"> select c.cid,c.cname from students s inner join middles m on s.sid = m.msid inner join courses c on m.mcid = c.cid and s.sname = #{name} </select> </mapper> <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="studentNamespace"> <resultMap type="cn.itcast.javaee.mybatis.many2many.Student" id="studentMap"> <id property="id" column="sid"/> <result property="name" column="sname"/> </resultMap> <select id="findAllByCourseName" parameterType="string" resultMap="studentMap"> select s.sname from students s inner join middles m on s.sid = m.msid inner join courses c on m.mcid = c.cid and c.cname = #{name} </select> </mapper> DAO package cn.itcast.javaee.mybatis.many2many; import java.util.List; import org.apache.ibatis.session.SqlSession; import cn.itcast.javaee.mybatis.util.MybatisUtil; /** * 持久层 * @author AdminTC */ public class StudentCourseDao { /** * 查询哈哈选学了哪些课程 * @param name 表示学生的姓名 */ public List<Course> findAllByName(String name) throws Exception{ SqlSession sqlSession = null; try{ sqlSession = MybatisUtil.getSqlSession(); return sqlSession.selectList("courseNamespace.findAllByName",name); }catch(Exception e){ e.printStackTrace(); throw e; }finally{ MybatisUtil.closeSqlSession(); } } /** * 查询java课程有哪些学生选修 * @param name 表示学生的课程 */ public List<Student> findAllByCourseName(String name) throws Exception{ SqlSession sqlSession = null; try{ sqlSession = MybatisUtil.getSqlSession(); return sqlSession.selectList("studentNamespace.findAllByCourseName",name); }catch(Exception e){ e.printStackTrace(); throw e; }finally{ MybatisUtil.closeSqlSession(); } } public static void main(String[] args) throws Exception{ StudentCourseDao dao = new StudentCourseDao(); List<Course> courseList = dao.findAllByName("哈哈"); System.out.print("哈哈选学了" + courseList.size()+"个课程,分别是:"); for(Course c : courseList){ System.out.print(c.getName()+" "); } System.out.println("\n-----------------------------------------------------"); List<Student> studentList = dao.findAllByCourseName("android"); System.out.println("选修了android课程的学生有"+studentList.size()+"个,分别是:"); for(Student s : studentList){ System.out.print(s.getName()+" "); } } } 总结 对于Mybatis的多表连接就非常简单了,由于SQL语句全是由我们自己写,如果我们返回的数据类型在当前的实体中是不够封装的话,那么我们只要再关联对应的映射属性就行了! 如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y 更多的文章可往: 文章的目录导航

优秀的个人博客,低调大师

C++学习笔记第二天:几个知识点

第一天,囫囵吞枣的把C++的基本概念撸了一遍 有几个地方不太理解,今天有针对性的学习一下 1、namespace 自定义命名空间,主要为了解决类、函数和全局变量的命名冲突问题。 调用某个命名空间里的符号时,需要使用格式:命名空间::符号名称 #include <iostream> using namespace std; namespace first { int var = 5; } namespace second { double var = 3.1416; } int main () { cout << first::var << endl; cout << second::var << endl; return 0; } 结果是53.1416两个全局变量都是名字都是var,但是他们不在同一个namespace中所以没有冲突。 关键字using可以帮助从namespace中引入名字到当前的声明区域 #include <iostream> using namespace std; namespace first { int x = 5; int y = 10; } namespace second { double x = 3.1416; double y = 2.7183; } int main () { using first::x; using second::y; cout << x << endl; cout << y << endl; cout << first::y << endl; cout << second::x << endl; return 0; } 输出是52.7183103.1416就如我们所指定的第一个x是first::x,y是second.y namespace也支持嵌套 需要注意的时,尽量不要在头文件中使用using namespace,因为头文件的引用具有不确定性,因此一旦在头文件中整体using了某个namespace,那么有可能会造成名称污染。 总而言之,using namespace 应尽可能的限制在小范围的作用域内。 2、头文件与源文件 头文件的后缀:.h,不需要编译,主要用于变量和函数的声明,而不必完成具体的实现,起到一个接口的作用。 源文件的后缀:.cpp,需要进行编译,主要存放函数的具体实现。 我的理解是: 在C++中,任何的符号(函数或者变量)都需要事先声明之后才可以调用。 假设有两个cpp源文件A和B,在A中定义了函数AFun,那如果B中需要调用这个AFun的话,也需要用同样的格式进行声明,然后才可以调用到AFun。 如果这个AFun是一个使用率很高的函数,或者A中还有很多类似的函数,那么所有需要这些函数的源文件都要被迫进行一系列的声明 头文件就是解决这个问题的,将A中所有使用率比较高的函数声明编写成一个独立的头文件,那其他源文件只需include这个头文件即可,无需将里面的函数再声明一遍。 值得注意的是,如果在头文件中声明变量,应该加上 extern 前缀,否则如果有两个以上的源文件引用这个头文件时,会提示编译错误。 有一篇文章说的非常好,链接在此:http://blog.csdn.net/chillon_liao/article/details/44261921 3、extern extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 'extern' 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候 第一个文件:main.cpp #include <iostream> int count ; extern void write_extern(); int main() { count = 5; write_extern(); } 第二个文件:support.cpp #include <iostream> extern int count; void write_extern(void) { std::cout << "Count is " << count << std::endl; } 最后输出: Count is 5 宠辱不惊,看庭前花开花落;去留无意,望天上云卷云舒

优秀的个人博客,低调大师

ASP.NET MVC WEB API必知必会知识点总结

一、理解WEB API:提供基于RESTful架构的WEB服务,通过HTTP请求方法(GET, PUT, POST, DELETE)映射到服务器端相应的ACTION方法(CRUD)。 RESTful架构: (1)每一个URI代表一种资源; (2)客户端和服务器之间,传递这种资源的某种表现层; (3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。 HTTP 的四个主要方法 (GET, PUT, POST, DELETE) 按照下列方式映射为 CURD 操作: GET 用于获取 URI 资源的进行展示, GET 操作不应对服务端有任何影响; PUT 用于更新 URI 上的一个资源, 如果服务端允许, PUT 也可以用于新建一个资源; POST 用于新建 资源, 服务端在指定的 URI 上创建一个新的对象, 将新资源的地址作为响应消息的一部分返回; DELETE 用于删除指定的 URI 资源。 二、WEB API特点: 1.CONTROL类继承自ApiController抽象类; 2.注册路由时一般无需指定ACTION节点,ACTION方法名称一般都包含HTTP请求方法名名称,路由系统通过HTTP请求方法自动寻找与之相应的ACTION方法并执行; 3.ACTION方法返回值一般为:JSON、XML或一般值对象 三、实现发送GET, PUT, POST, DELETE HTTP请求方法 1.通过JQUERY.AJAX方法指定TYPE类型来实现GET, PUT, POST, DELETE HTTP请求方法; 2.直接访问URL或将表单的METHOD方法设为GET,则可实现GETHTTP请求方法; 3.将表单的METHOD方法设为POST,则可实现POST HTTP请求方法; 4.PUT、DELETE除第一种方法外,只能通过先在服务端重写HTTP请求方法(自定义HttpMessageHandler来实现),然后再在客户端请求报文头指定“X-HTTP-Method-Override ”值为PUT或DELETE来实现;具体实现方法详见:如果调用ASP.NET Web API不能发送PUT/DELETE请求怎么办? 5.在注册WEB API路由规则时指定ACTION节点; 四、WEB API请求与服务端处理实现方法: 1.GET ALL方法: 客户端: 1 2 3 4 5 6 7 8 9 10 $( "#Button1" ).click( function () { $.getJSON( "@Url.Content(" ~/api/values ")" , function (data) { var rs = "" ; $.each(data, function () { rs += this + "," ; }) alert(data); showResult(rs); }) }); 服务器端: 1 2 3 4 5 // GET api/values public IEnumerable<string> Get() { return new string[] { "value1" , "value2" }; } 2.GET ONE方法: 客户端: 1 2 3 4 5 6 $( "#Button2" ).click( function () { $.getJSON( "@Url.Content(" ~/api/values/5 ")" , function (data) { alert(data); showResult(data); }) }); 服务器端: 1 2 3 4 5 // GET api/values/5 public string Get(int id) { return "value is " + id.ToString(); } 3.POST CREATE方法:(注意以下客户端中的第几种方法就对应服务器端的第几种方法) 客户端: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 //第一种: $( "#Button1" ).click( function () { $.post( "@Url.Content(" ~/api/values ")" , {name: 'zwj' ,age:29}, function (data) { alert(data); showResult(data); }) }); //第二种: $( "#Button3" ).click( function () { $.ajax( "@Url.Content(" ~/api/values/1 ")" , { type: 'post' , data:JSON.stringify({ name: 'zwj' , age: 29 }), contentType: 'application/json' , //dataType: 'json', success: function (result, status, xhr) { alert(result); showResult(result); } }) }); 服务器端: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 //第一种方法: public string Post() { string s = "" ; HttpContextBase context = (HttpContextBase)Request.Properties[ "MS_HttpContext" ]; //获取传统context HttpRequestBase request = context.Request; //定义传统request对象 for ( int i = 0; i < request.Form.Keys.Count; i++) { s += string .Format( "{0}={1}<br/>" , request.Form.Keys[i], request.Form[i]); } return "Post values:" + s; } //第二种方法: public string Post([FromBody]Person p) { return string .Format( "Put values:name:{0},age:{1}" + p.Name,p.Age); } 4.PUT UPDATE方法: 客户端方法与POST方法相同,只是TYPE指定为:PUT; 服务器端与POST方法相同; 5.DELETE 方法: 客户端方法与GET方法相同,只是TYPE指定为:DELETE; 服务器端与GET方法相同; 也参见这篇文章:ASP.NET MVC学习系列(二)-WebAPI请求 本文转自 梦在旅途 博客园博客,原文链接:http://www.cnblogs.com/zuowj/p/4769315.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

整理大型网站架构必知必会的几个服务器知识

最近看书及系统开发部署过程中的一些心得,再对照自己之前的从业经验,很多都是听闻而已,当然也有一些已经很熟悉,有的正在搞,有的未来希望可以着手付诸实施,留此存照。 1、负载均衡服务器 负载均衡服务器主要作用是实现某些类型服务器的规模扩展。比如对于系统前端的web服务器和后端的数据库服务器,想通过加服务器实现N+1横向扩展,通过多台服务器负载分担压力,负载均衡必不可少。 2、web服务器 最常见,内存要求不是很高但cpu要求较高,主要用于部署各种web应用,如带界面的web页面、不带界面的web服务、wcf等等。 3、缓存服务器 大中型网站,分布式缓存已是标配,缓存服务器专门用于部署分布式缓存,一般而言对内存和带宽要求较高。 4、消息队列服务器 队列是系统解耦利器,也是大中型分布式系统标配,没有队列,业务系统很容易高度耦合,系统吞吐量也会很快遭遇瓶颈。 5、文件服务器 分布式文件系统,专门用于存储业务系统需要的各种文件如图片、多媒体文件等。 6、索引服务器 用于网站全文索引,搜索必备。对内存和CPU要求较高,大型网站,通常还需要支持主从备份和容错,甚至多实例索引集群。 7、搜索服务器 通常需要部署多台,否则查询多了性能撑不住,对内存要求不高。有的中小型站点,索引和搜索服务器在物理和逻辑上都是同一台服务器。 8、作业服务器 主要用于后端应用程序大批量大数据量复杂业务逻辑的定时作业,大多数互联网公司标配,某些企业的定时调度框架是直接部署在web服务器上的,可以减少这里的所谓作业服务器。 9、数据库服务器 主要用于存储和查询数据。数据库已是各种系统实际上的标配,内存和CPU都要求极高,网络和硬件要求也不低。大中型网站还需要支持数据库的主从备份和容错,甚至多实例的数据库集群。 通常,大中型的互联网应用会经历一个从单一的数据库服务器,到Master/Slave主从服务器,再到垂直分区(分库),然后再到水平分区(分表,sharding)的过程。而在这个过程中,Master/Slave以及分库相对比较容易,对应用的影响也不是很大,但是分表会引起一些棘手的问题,比如不能跨越多个分区join查询数据,如何实现DB负载等等,这个时候就需要一个通用的DAL框架来屏蔽底层数据存储对业务逻辑的影响,使得底层数据的访问对应用完全透明化。 10、nosql服务器 海量数据处理的兴起,各种nosql产品层出不穷,nosql服务器主要用于处理海量数据,支持存储、查询、分片等。 web应用中,有两个一直是不好实现横向扩展或者由于历史遗留问题实现代价非常大的东西,如你所知,就是:A、数据库 B、网络带宽。 而某些nosql的出现很可能解决这个历史遗留难题,现在已经有nosql产品弥补了关系型数据库天生不支持横向扩展的缺点,在特定场景下正在替代关系型数据库。 11、其他 需求不断变化和应用需要,某些互联网企业还可能衍生出基于安全的授权/证书服务器,全局唯一的流水号服务器,会话服务器等等。 参考: <<大型网站技术架构>> <<构建高性能web站点>> http://www.cnblogs.com/terryli/archive/2008/04/06/1139121.html http://www.cnblogs.com/ejiyuan/archive/2010/10/29/1796292.html http://kb.cnblogs.com/page/99549/ http://highscalability.com/blog/2014/7/21/stackoverflow-update-560m-pageviews-a-month-25-servers-and-i.html http://www.infoq.com/articles/perera-data-storage-haystack http://lethain.com/introduction-to-architecting-systems-for-scale/ 本文转自JeffWong博客园博客,原文链接:http://www.cnblogs.com/jeffwongishandsome/p/some-knowledge-about-server.html,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册