精华内容
下载资源
问答
  • 当你爸妈吐槽你的微信头像时,你该如何反击?
    2019-05-29 12:00:00


    点击上方“Python爬虫与数据挖掘”,进行关注

    回复“书籍”即可获赠Python从入门到进阶共10本电子书

    态度决定高度,格局决定结局。

    几年前,我们给爸妈手机上下载了一款神奇的软件,他的名字叫微信。几年后,爸妈就开始吐槽我们的微信头像了。

    爸妈对我们微信头像的关注程度远胜过我们的衣食住行,我在新浪微博上看到了这张统计图。

    640?wx_fmt=jpeg

    图片来源:新浪微博

    所以,你就会被催着经常换头像了。

    640?wx_fmt=jpeg

    图片来源:新浪微博


    但是,如果你想来个「绝地反击」、「以牙还牙」,让他们换头像,那你还是太嫩了。

    640?wx_fmt=jpeg

    图片来源:新浪微博

    640?wx_fmt=gif

    怎么办啊?身为技术人儿,我们当然不会被这些问题难住。今天,pk哥就教你如何用技术来个「绝地求生」。

    我要让爸妈看看我所有微信好友的头像都是什么样,让他们学会适应,适应我们年轻人的头像。

    640?wx_fmt=gif

    所以,接下来我要做的,就是收集我微信好友所有好友的微信头像,并把这些头像全部整合在一张图上,然后我们保存或者收藏。下次,你爸妈让你再换头像时,哼哼,你就把这大图发给他们。

    640?wx_fmt=png

    以上是我的所有微信好友头像的「全家福」,微信好友比较多。跟着 pk 哥学,分分钟教会你做一张属于你自己微信圈所有好友头像的「全家福」。

    项目环境

    语言环境Python 3.6

    导入库:itchat、PIL

    因为我们要获取微信头像,所以我们又要请出我们的老朋友 itchat 库了,这个库应该不陌生了,因为 pk 哥之前用它做过微信机器人:用 Python 玩微信,机器人陪你唠嗑;用它绘制过微信好友的签名的词云图:Python爬取微信,我发现了之前没发现的秘密!;用它查看好友撤回的消息:Python骚操作 | 还原已撤回的微信消息;用它给你的女神说早安或者晚安:你还单身?Python 教你脱单

    头像收集完成后,我们还有对所有头像进行合成处理,所以我们还要引入一个 PIL 图像处理库。

    所以,有这么强大的库支持我们,我们一定可以「绝地反击」、「反败为胜」的,相信我,嘿嘿。

    以上说的库请自行用 pip 命令安装好,导入就行。

    import itchat
    import PIL.Image as Image
    import math
    import os

    登录微信

    我们首先用 itchat 库登录我们的微信,获取所有好友信息。

    itchat.auto_login(True)  # 登录微信
    friends = itchat.get_friends(update=True)[0:]

    保存头像

    itchat 库中有个自带的获取微信头像的方法:get_head_img,直接调用即可。获取头像后,我们把头像以数字递增命名并保存在我们规定的目录中即可。

    640?wx_fmt=png

    上面这个保存图片的函数,我们看到有个参数,是保存图片的主目录,这个主目录如果不存在,我们就创建一个。我们单独写一个函数来定义这个目录并返回它。

    640?wx_fmt=png

    以上两个简单的函数,我们就把微信好友的所有图像保存到指定的目录了。

    头像合成

    接下来我们需要做的是把这所有的头像制作成「全家福」。

    这时,该另外一个库出场了,他就是图片处理库:PIL 库。

    接下来,用这个库,新建一个一个白色背景的新图片,根据图片的大小,把所有图片按照算好的尺寸填入到这个大画布中。处理完成后,把「全家福」也保存在头像同样的目录下。

    640?wx_fmt=png

    再然后,接下来就不用 pk 哥我教了吧,把「全家福」发到自己微信,保存或者收藏。下次,你妈再喊你换头像时,你就把这张全家福发给她,告诉她,别人的头像都是这样的,我就不换,哼哼。

    不说了,我去给我妈发制作好的微信头像「全家福」了,下次再聊。

    640?wx_fmt=gif

    想要获取本文完整版代码,请在公众号后台回复「微信头像」获取。

    ------------------- End -------------------

    往期精彩文章推荐:

    640?wx_fmt=png

    欢迎大家点赞,留言,转发,转载,感谢大家的相伴与支持

    想加入Python学习群请在后台回复【入群

    万水千山总是情,点个【在看】行不行

    更多相关内容
  • 虽然微信改变了我们很多生活方式,但这些都抑制不住小编想要吐槽的洪荒之力。不知道你们会不会这样,反正小编已经受不了了,已开启吐槽模式! 1、不能双向删除好友 记得前阵子,好友跟小编说,曾经很要好的初中...

    虽然微信改变了我们很多生活方式,但这些都抑制不住小编想要吐槽的洪荒之力。不知道你们会不会这样,反正小编已经受不了了,已开启吐槽模式!


    1、不能双向删除好友

    记得前阵子,好友跟小编说,曾经很要好的初中同学快要结婚了。小编心想,之前关系挺好的,虽然很长时间没联系了,但总归要祝福一下吧,所以小编就戳了一下初中同学的微信。

    结果!XXX开启好友验证,你还不是他(她)的好友!


    祝福的话没发出去,反而受到了一百万点暴击!话说为啥对方删了我,还在我的通讯录中继续(活着),就不能将他也从我的好友中删除吗?!!!非要这样对我!真是扎心了!


    2、拉黑没有提示

    说到这个,小编就真的很烦了,天天被群发“ 没事!不用回我。我只是要清一清有没有人拉黑/删除我而已”。这样真的很烦啊!为啥不给个拉黑自动提示的功能呢?虽然小编也很想把那些“躺尸”给灭了,但不想使用“清一清”功能得罪人好伐,很容易友尽的!


    3、撤回消息有提示

    微信君,虽然我很喜欢你的撤回功能,这样可以让我撤回一些发错的文字或图片。但是!你撤回就撤回,干嘛还留下提示呢?去一半留一半的不尴尬吗?不知道其实很多人的好奇心都很重吗?不知道被人追问撤回的内容是什么很烦吗?


    4、不能快速切换账号

    账号的切换也是一个满满的吐槽点!每次切换账号都要先退出账号!不麻烦吗?不累吗?不能想QQ一样也能快速切换账号吗?


    5、表情包上限

    要斗图?来啊!谁怕谁啊!没WiFi还有4G流量呢!但请你告诉我!表情包上限是什么鬼?斗着斗着没图了怎么破?


    6、通讯录不能分组

    一直不明白,微信为啥不能好友分组,全靠微信昵称或备注的首字母排名。每次找人要么用搜索功能找,要么按首字母去找,真的很麻烦哟!万一对方换了昵称,还得在茫茫好友中寻找,就问!设个好友分组很难吗?


    7、强行进群

    被好友拉进一个陌生群,相信大家都遇到过的吧!小编真的是贼讨厌贼反感这种被强迫的感觉!未经本人允许,就被拉到新群里。如果全是熟人,那还好,拉到一个满满都是陌生人的群,那才叫一个尴尬呢!

    8、不能屏蔽群消息

    除了强行进群之外,让小编受不了的还有不能屏蔽群消息!

    对于一些既不能退群,又不想收到消息的群,小编都是消息免打扰的。但是!干嘛要给个小红点在对话框那里!不知道强迫症患者看到这些会很烦燥吗?


    9、将对话框删除,连聊天记录都没了

    聊天列表中,总是保留着平时聊天的对话框。有时候真的很喜欢将微信聊天对话框删掉,这可以让消息列表看起来简洁一点。但是微信君,给我留下聊天记录会死啊?干嘛要对话框和聊天记录共存亡?这样你让我很难办耶!


    有时候意外删除了聊天记录,还得费心思去找强力苹果恢复精灵找回聊天记录,真不省心。


    10、强制绑定银行卡

    微信上没有绑定银行卡,红包金额是有限的,单日上限200元,单月上限500元!虽然达到单日上限后,第二天可以再发是呢!超过500就会达到单月上限哟。这让没有银行卡的朋友怎么办?

    11、提现要钱

    提现要钱!微信提现的免费额度只有一千块钱,而且是终生只有这一千的免费额度!小编穷!小编很不喜欢提现要收手续费!你们觉得呢?


    12、没有消息已读提示

    发给好友消息后就没声没息,也不知道消息看了没有。等久了很急,你造吗?

    13、朋友圈不能发动图

    有时候心血来潮想发个动态图,微信君,你为啥不给我这个机会?!

    14、朋友圈转发,点赞

    经常有朋友发过来的消息是这样的,“麻烦朋友圈第一条转发/赞评哦~么么哒!”。这种事相信绝大部分都遇到过,不过每次都要经过“点开好友头像——点开个人相册——点右下角点赞/评论区——再点赞”,这样的操作会不会略显麻烦了点呀。

    微信君,敢不敢给我一个直接给个点赞或评论的功能呀!


    15、不能特别关注群成员

    就想关注微信群里的某个人,别问我原因!(你们都懂的)


    以上这么多吐槽点说说看你中了几个呢?反正小编是全中了!

    展开全文
  • 百万博主之星,帅地被吐槽开挂了?

    千次阅读 多人点赞 2021-01-13 15:32:33
    还被大佬们吐槽开挂了 今天帅地一看票数,在各位大佬的帮助下,居然挤进了第一(还得亏敖丙大佬今天还没发力) 很多人帮帅地发了朋友圈,也把所有票都投给了帅地,真心感动啊,只能说,你们的一举一动,帅地都记在...

    2017 年 3 月 25 号,帅地在 CSDN 写了人生的第一篇不完整博文

    在这里插入图片描述
    如果你是我的校友,并且也经历过转专业的话,可能你会发现,3 月份是学校转专业的时间段。

    是的,2017 年 3 月份,帅地刚刚从木材科学与工程转到了软件工程,成功入坑编程之后,我就把所有精力都花在学编程这件事上了。

    当时很多人都说每个程序员都应该要有自己的博客,于是,我跑去 CSDN 注册了一个博客,写下了这篇 C 语言相关的文章。

    然而,这篇文章并没有写完,帅地跟大家说过,我的文章从来都不是写给自己看的,而是写给别人看的,如果仅仅是做笔记,那么完成一篇博文会很快,但我就是要把它彻底讲懂,让没有学过 scanf 和 gets 函数的人,也能够看的下去,这无疑让我花了很多时间。

    于是,我写了一半就放弃了,写文章实在是太花时间了,关于写文章这件事,或许你可以看我另外一篇文章:曾经一度困扰我:有没有必要写博客?

    2018 年 5 月 28 号,帅地在 CSDN 写下了人生第一篇完整的文章

    在这里插入图片描述

    是的,这篇短短一两千字的文章,花了我四五个小时,在写下这篇文章之后,帅地又放弃写文章了,对于一个从来不喜欢做笔记,高中作文在及格边缘徘徊的人来说,写文章,太难了。

    2018 年 7 月 27 号,帅地又再次踏上了写文章的道路

    在这里插入图片描述

    当时,正处于暑假中,帅地想逼自己一把,人生的关键,可能就是你一个不经意的决定。是的,帅地从那时开始在微信公众号写文章,是不相满,文章阅读量惨不忍睹,从 2018.7 到 2018.9,帅地的公众号只有 300 多粉丝,这期间也会把文章同步到 CSDN,但我只能告诉你,我 CSDN 的文章阅读量是个位数,你现在看到的几千阅读量,是因为我后面火了,很多人翻我历史文章导致的。

    写的文章没人看,很难受,写过文章的人估计深有体会,被人喷了更加难受。2018 年在 CSDN 发了几篇文章之后,就把它放弃了,没办法,真心没人看,自认为文章还写的不错,可能是 CSDN 的推荐机制不大完善。

    到了 2019 年 10 月末,帅地秋招结束了,拥有大量的时间,并且 CSDN 貌似也改版了,于是帅地再次强势回归 CSDN,卧槽,不得了,我的文章火的一塌糊涂,那几个月,我觉得我是 CSDN 最火爆的男人了,仅仅只用了 20 多天,便出现了一篇 10w+ 阅读量的文章,当时我还发了个朋友圈

    在这里插入图片描述

    至于文章的阅读量?别问,问就是百万阅读

    在这里插入图片描述
    两篇文章斩获 150w+ 的阅读量,这应该算顶流了吧?

    可能是因为,我的文章,真的是用心写给别人看的吧,当时随便找一篇,都是上万阅读量,收藏也是几千几千的

    在这里插入图片描述

    CSDN 的火爆,也让我的微信公众号实现了逆袭,给大家看一下当时的涨粉速度

    在这里插入图片描述

    秋招找到工作,从来都不是终点,而是帅地发力的起点,是的,我很庆幸自己拿到 offer 之后,没有过上颓废的生活,而是集中精力来发力。

    在这段时间,帅地的微信差点被搞炸,当时我在文章留了一些电子书,电子书很容易失效,所以我让他们加我微信好友获取,然而每天有好几百人加我好友,我得一个个手动通过好友请求,还得一个个给他们发资料。

    微信每次最多只能给 9 个好友发消息,这让我很难受, 为了解决这个问题,我拉了群,把资料放群公告,方便更新,但是拉群又是谈何容易,每次最多只能拉 40 个人进去,并且你还得记得谁拉了谁还没拉。

    所以帅地给每个加好友的读者打了标签,每 40 个人成一个标签组,给大家看一下我打的标签

    在这里插入图片描述

    通过好友=》打标签=》拉群,帅地每天点到脖子都不舒服了,但痛并快乐着!

    当时整了很多群

    在这里插入图片描述

    然而有些人一看到要加群,特么以为我要干嘛,直接把我拉黑了,,我一脸懵逼,帅地可以自豪着告诉大家,帅地从来没有在群里发过广告,打过任何推广,除了每天更大家更新失效的链接

    在这里插入图片描述

    为了给这些人发资料,帅地当时加了很多人,以至于微信被加满了,很多人想加帅地,我都没有通过好友请求

    在这里插入图片描述

    微信人太多了,每天都有人问问题,帅地也不可能都回复,但你们的每一个问题,帅地都会看,觉得可以回复的就回复,很难回复或者不想回复的就不回复了,然而有时候回复慢了还被拉黑

    在这里插入图片描述

    帅地哭晕在厕所了,每天各种奇葩问题都有,不行你看

    在这里插入图片描述

    虽然帅地会在公众号发一些广告,这我不否认,喷也被你们喷了,但公众号背后的维护成本,当你有 10 万粉丝,一万多微信好友的时候,你就知道了,没有一个强大的内心,想不奔溃都难。

    帅地从来就没有引导大家来加我的微信,我也懒的去维护,但每天依然有好多人来加我,咨询我问题,但我的微信坑位确实是满了,每天也会在空闲时偶尔删除一些人来腾出空位

    在这里插入图片描述

    为了 满足大家,帅地昨晚连夜整了一个企业微信,今天放出 500 个坑位给大家加,其实加我微信我也不一定会回复你,希望你能理解,但是,如果你是我的读者,并且加过我微信,还给我点过赞,那么我可能会记住你的头像和昵称,往后你需要我帮忙或者咨询我一些问题,我还是会尽最大的努力来帮你。

    但是你突然来加我,一上来就让我帮你,不好意思,帅地假装没看到信息。

    好像说的有点现实了,但这确实是事实,帅地每天面对太多人了,肯定优先帮助那些支持过我,朋友圈给我点过赞,微信加过好友,聊过天的读者,可能这算是微信好友的一个好处吧。

    不过这次是企业微信,后面拉群发福利,例如过年过节拉群发红包,后面有开直播等等方便通知,消息也可以保存很久,所以呢,整了个企业微信,想跟帅地做个点赞之交的,可以加,下次开放二维码估计就能春节之后了

    在这里插入图片描述

    是的,从 CSDN 写博客,到写微信公众号,再转战 CSDN,最后处理各类读者问题,我只能说,只能经历过,才能知道有多不容易,这些经历,让帅地认识了很多人,也让帅地的内心更加强大了,这期间的故事,有机会再跟讲价帅地遇过的坑,玩过的骚技巧吧!

    对了,CSDN 博客之星正在进行,帅地也参与了,本来帅地是不想理的,因为拉票太费劲了,而且拉票周期长达半个月,不过居然参加了,就好好拉一下票吧,帅地昨天花了九牛二虎之力在朋友圈和微信群拉了一波,瞬间从百名之外挤进了前三,还被大佬们吐槽开挂了

    在这里插入图片描述

    今天帅地一看票数,在各位大佬的帮助下,居然挤进了第一(还得亏敖丙大佬今天还没发力)

    在这里插入图片描述

    很多人帮帅地发了朋友圈,也把所有票都投给了帅地,真心感动啊,只能说,你们的一举一动,帅地都记在心里。

    投票时间是 2021.1.11 ~ 2021.1.25,每一天都可以投票,帅地从来没有参与过这东西,第一次参加,希望大家能够给帅地投一个票,如果每天都帮我投,那我就更开心了,让帅地开一次真挂给他们看看

    链接直达:https://bss.csdn.net/m/topic/blog_star2020/detail?username=m0_37907797

    每一个人的投票都会被记录在列表里,听说投了票的无论是加好友还是请求帮忙都会更加容易,是时候验证有哪些真爱粉了(疯狂暗示大家投票拉票)

    展开全文
  • 看的人欢迎吐槽。我会从以下几个方面进行总结。(总结的是我目前认为的以及怎么做的) 这个项目设计步骤是什么 这个项目用到的知识有什么 这个项目遇到了什么问题,怎么解决的。 这个过程中收获的知识点有哪些 文章...

    前段时间学习完了MySQL和JDBC,想着自己做个东西实战一下,于是写了一个烂大街的图书管理系统。。。但好歹是自己一个人日夜兼程,硬着头皮做出来的还像样的东西,总结一手吧。看的人欢迎吐槽。我会从以下几个方面进行总结。(总结的是我目前认为的以及怎么做的)

    • 这个项目设计步骤是什么
    • 这个项目用到的知识有什么
    • 这个项目遇到了什么问题,怎么解决的。
    • 这个过程中收获的知识点有哪些

    一、这个项目设计步骤是什么

    管理信息系统的建立与应用可以划分成总体规划、系统开发和系统运行三个阶段,其中系统开发阶段还可进一步分为系统分析、系统设计和系统实施等工作环节。上述各个阶段排列成一个严格的线性开发序列,在每个工作阶段均产生完整的技术文档作为下一阶段工作的指导和依据,每一阶段都应对文档进行评审,确信该阶段工作已完成并达到要求后才能进入下一阶段,同时在以后的工作中不能轻易改变前面经过评审的成果。
    国外曾有人对一些软件项目开发各阶段的工作量进行了统计,结果表明,在开发过程中各工作阶段所占全部工作量的比重分别达到系统分析:系统设计:编程:模块调试:系统调试=20%:15%:20%:25%:20%。由此可见,程序编写在开发工作中只占很小比例,而调试工作却占整个开发工作量的一半左右

    在做这个图书管理系统之前,我大概看了几篇相关的论文,我的大致的步骤如下:

    1、明确开发环境

    MySQL8.0.21、IDEA开发工具、JDK11.0、JDBC、SpringJDBC-Template、DBCP开源数据连接池
    图形界面使用Swing组件
    对每个工具有认识、懂得如何使用。(swing不太懂就扑街了😭)

    2、系统分析步骤

    需要考虑:①系统的处理对象;②系统的功能、具体的业务;③对具体的业务有流程图设计;④将业务需求进行模块划分
    处理对象:

    • 图书信息:书籍编号,书名,类型,作者,译者,出版商,出版时间,价格,存储量。
    • 读者信息:编号,姓名,性别,最多可借数量,可借时间。
    • 借阅信息:读者编号,书籍编号,借阅日期,还书日期,借阅时间,是否还书。
    • 管理员信息:编号,姓名,密码,是否为管理员。

    系统功能、业务
    从处理对象的角度去分析

    • 系统管理员能够对用户实施管理:包括添加、删除用户信息,对用户密码进行有效管理。
      还能够方便的进行图书管理:包括设置图书种类和图书信息,添加、删除和修改图书信息。
    • 读者用户在该系统进行借书,系统自动添加借书记录和借书日期,读者还书时管理员也能在平台进行还书操作,修改读者借书信息。
    • 管理员可以修改用户信息和自己的信息,还可以对数据库的信息进行维护、日志管理等
    • 游客和读者都能方便的对图书进行查询。但只有登陆了才能进行借书操作

    流程图设计
    我没有设计,按照自己的思路写的。

    功能模块划分

    • 应用启动界面模块
    • 用户注册模块
    • 用户信息登陆模块
    • 用户借阅信息
    • 图书馆藏资源模块
    • 管理员模块

    具体功能还需要细分、设计实现

    3、数据库设计

    大写强调 ;
    设计数据库字段用英文 !

    我们要设计数据库要考虑

    • 表结构的实现(主键、非空、自增、数据类型、外键约束等)
    • 每个表插入和删除、修改、查询的时候需要注意什么
    • 触发器和事务是否需要设计

    附上我的表结构:
    在这里插入图片描述

    books表
    书编号(主键)、书名、价格、剩余量、出版商、作者、出版日期、类型

    在这里插入图片描述
    user表
    字段:id主键、用户名、密码、是否管理员(这里可以添加权限,我没有设计,0非管理员1管理员)

    在这里插入图片描述
    reader表
    id关联user表的id,名字、可借书的天数、可借书本数、性别、已借书数、人生格言(默认值)
    在这里插入图片描述
    borrow表
    借书者的id、所借书id、借书日期、是否归还、截止还书日期
    在这里插入图片描述

    创建的触发器
    1、当书的余量为0,读者想要借书无法借
    2、当读者成功借书、则余量-1且borrow表添加对应的借书记录
    3、当读者还书时、则余量+1、且borrow表删除对应的记录
    4、当添加图书时,检查出版日期不能超过今天

    创建的表结构的MySQL语句汇合:
    创建book表——>创建user表——>创建reader表——>创建borrow表

    1、Books表
    创建:
    CREATE TABLE `books` (
       id int NOT NULL,
       name varchar(100) NOT NULL,
       price float NOT NULL DEFAULT '0',
       surples int NOT NULL DEFAULT '0',
       publisher varchar(200) DEFAULT NULL,
       author varchar(50) DEFAULT NULL,
       publish timestamp NULL DEFAULT NULL,
       type varchar(50) DEFAULT NULL,
       PRIMARY KEY (id)
     ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
    
    触发器:
    create trigger before_booksInsert before insert on books for each row
    begin
     if new.publish >=now() then
     	SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'publish Time is after nowadays!' ; 
     end if;
     end
    
    -----------
    
    2、borrow表
    创建表:
    CREATE TABLE `borrow` (
       reader_id int NOT NULL,
       book_id int NOT NULL,
       borrow_date timestamp NULL DEFAULT NULL,
       wether_return varchar(2) DEFAULT NULL,
       return_date timestamp NULL DEFAULT NULL,
       PRIMARY KEY (reader_id,book_id),
       KEY book_id (book_id),
       CONSTRAINT `borrow_ibfk_1` FOREIGN KEY (reader_id) REFERENCES `reader` (id) ON DELETE CASCADE ON UPDATE CASCADE,
       CONSTRAINT `borrow_ibfk_2` FOREIGN KEY (book_id) REFERENCES `books` (id) ON DELETE CASCADE ON UPDATE CASCADE,
       CONSTRAINT `borrow_chk_1` CHECK (( wether_return in (_utf8mb4'yes',_utf8mb4'no')))
     ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
    
    
    触发器:
    create trigger before_borrowupdate before update on borrow for each row
    begin
     if old.borrow_date is not null then
     	set new.borrow_date=old.borrow_date;
     end if;
     end
    
    触发器:
    create trigger after_borrowinsert after insert on borrow for each row
    begin
     update books set surples=surples-1  where  id=new.book_id;
     end
    
    触发器:
    create trigger before_borrowinsert before insert on borrow for each row
    begin
     declare c int;
     set c = (select surples from books where  id=new.book_id);
     if c=0 then
      SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'There is no books';
      end if;
     end
    
    ---------
    3、user表
    创建表:
    CREATE TABLE `user` (
       id int NOT NULL,
       name varchar(45) NOT NULL,
       password varchar(45) NOT NULL,
       isAdmin smallint DEFAULT NULL,
       PRIMARY KEY (id)
     ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
    
    4、reader
    创建表;
    CREATE TABLE `reader` (
       `id` int NOT NULL,
       `name` varchar(20) DEFAULT NULL,
       `borrow_day` int NOT NULL DEFAULT '90',
       `borrow_num` int NOT NULL DEFAULT '30',
       `sex` varchar(6) DEFAULT NULL,
       `life_motto` varchar(45) DEFAULT '读万卷书,行万里路!',
       PRIMARY KEY (`id`),
       CONSTRAINT `reader_chk_1` CHECK ((`sex` in (_utf8mb4'man',_utf8mb4'woman')))
     ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
    
    这里可以添加了一个外键:reader id 关联了user的id不然需要在设计程序的时候注意
    

    这个过程中遇到的错误:

    • 1、MySQL不支持在触发器使用事务,也就是不允许使用rollback等操作。可以使用抛出异常来替代
    • 2、在MySQL WorkBeach里面添加触发器时需要修改sql语句结束符,该语句要和创建触发器的语句一起,不要单独执行。都写好了以后点击执行。
    • 3、注意数据类型,TIMESTAMP和DATE,TIMESTAMP日期包含具体几点,格式比较长。尽量使用DATE,方便与java里面的数据类型进行转化。
    • 4、注意new和old的使用场景以及针对的对象表,inset和update、delete三者的new区别。
    • 5、判断日期的前后使用对应的函数:mysql 日期加减天数
    MySQL 为日期增加一个时间间隔:date_add()
    select date_add(now(), interval 1 day); -1天
    select date_add(now(), interval 1 hour); -1小时
    select date_add(now(), interval 1 minute); -1分钟
    select date_add(now(), interval 1 second); -1秒
    select date_add(now(), interval 1 microsecond);-1毫秒
    select date_add(now(), interval 1 week);-1周
    select date_add(now(), interval 1 month);-1月
    select date_add(now(), interval 1 quarter);-1季
    select date_add(now(), interval 1 year);-1年
    MySQL adddate(), addtime()函数,可以用date_add() 来替代。
    2. MySQL 为日期减去一个时间间隔:date_sub()
    MySQL date_sub() 日期时间函数 和date_add() 用法一致。
    MySQL 中subdate(),subtime()函数,建议,用date_sub()来替代。
    

    4、DAO三层架构模型实现

    我们需要将与数据库的交互进行封装
    在java里面创建表映射对象,我们对对象的操作就可以操作对应的表数据了。

    一、ORM表映射

    如books表的映射

    package DatabasesOperation.DAO_Design.ORM;
    
    import java.util.Date;
    
    /**
     * ORM映射books表
     */
    public class ORM_Books {
        private int id;//书的编号
        private String name;//书名
        private float price;//价格
        private int surples;//剩余量
        private String publisher;//出版商
        private String author;//作者
        private Date publish;//出版日期
        private String type;//类型
        public static String[] columnNames = {"编号", "书名", "作者", "价格/元", "出版社", "出版日期", "类型", "余量"};//book的数据
    
        public ORM_Books() {//空参构造器
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public float getPrice() {
            return price;
        }
    
        public void setPrice(float price) {
            this.price = price;
        }
    
        public int getSurples() {
            return surples;
        }
    
        public void setSurples(int surples) {
            this.surples = surples;
        }
    
        public String getPublisher() {
            return publisher;
        }
    
        public void setPublisher(String publisher) {
            this.publisher = publisher;
        }
    
        public String getAuthor() {
            return author;
        }
    
        public void setAuthor(String author) {
            this.author = author;
        }
    
        public Date getPublish() {
            return publish;
        }
    
        public void setPublish(Date publish) {
            this.publish = publish;
        }
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        @Override
        public String toString() {
            return "Books[id:" + getId() + " name:" + getName() +
                    " author:" + getAuthor() + " surples:" + getSurples()
                    + "publisher:" + getPublisher() + " publish:" + getPublish() + " price:" +
                    getPrice() + "type" + getType() + "]";
        }
    
        /**
         * 只要传入的string数组的属性与books的属性一一对应,我们就可以添加图书
         *
         * @param res 传入的属性数组
         */
        private void setBooks(String[] res) {
            String s;
            int i = 0;
            while (i < res.length) {
                s = res[i];
                switch (i) {
                    case 0:
                        int id = Integer.parseInt(s);
                        this.setId(id);
                        break;
                    case 1:
                        this.setName(s);
                        break;
                    case 2:
                        float price = Float.parseFloat(s);
                        this.setPrice(price);
                        break;
                    case 3:
                        int surples = Integer.parseInt(s);
                        this.setSurples(surples);
                        break;
                    case 4:
                        this.setPublisher(s);
                        break;
                    case 5:
                        this.setAuthor(s);
                        break;
                    case 6:
                        java.util.Date publish = java.sql.Date.valueOf(s);
                        this.setPublish(publish);
                        break;
                    case 7:
                        this.setType(s);
                        break;
                    default:
                        break;
    
                }
                i++;
            }
        }
        public static String[] booksToArray(ORM_Books books){//将一本书各个字段提取出来,变成一维数组
            return new String[]{
                    Integer.toString(books.getId()),
                    books.getName(),
                    books.getAuthor(),
                    Float.toString(books.getPrice()),
                    books.getPublisher(),
                    books.getPublish().toString(),
                    books.getType(),
                    Integer.toString(books.getSurples())
            };
    
        }
    }
    
    

    我们把这些表映射放到统一的包ORM里面。
    在这里插入图片描述

    二、数据库连接池CP

    在Java中开源的数据库连接池有以下几种 :
    1、C3P0:是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate [2] 一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。
    2、Proxool:是一个Java SQL Driver驱动程序,提供了对选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中,完全可配置,快速、成熟、健壮。可以透明地为现存的JDBC驱动程序增加连接池功能。
    3、Jakarta DBCP:DBCP是一个依赖Jakartacommons-pool对象池机制的数据库连接池。DBCP可以直接的在应用程序中使用。
    4、DDConnectionBroker:是一个简单、轻量级的数据库连接池。
    5、DBPool:是一个高效、易配置的数据库连接池。它除了支持连接池应有的功能之外,还包括了一个对象池,使用户能够开发一个满足自己需求的数据库连接池。

    我们使用是DBCP
    在这里插入图片描述
    利用该数据库连接池我们创建封装好的JDBC实例:
    相关的配置我们写到一个文件里面:
    在这里插入图片描述
    然后我们创建工具类用于获取数据库连接池的datasource

    package DatabasesOperation.JDBCUtils;
    
    import org.apache.commons.dbcp2.BasicDataSourceFactory;
    
    import javax.sql.DataSource;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
    
    /**
     * 创建一个工具类,用于创建jdbc加载驱动,释放资源等,优化Test_Driver代码
     */
    
    public final class JDBCUtils {
        private static DataSource datasource;
    
        public static Connection getConnect() throws SQLException {//异常应该抛出
            return datasource.getConnection();
        }
        static {
            try {
                InputStream inputs = JDBCUtils.class.getClassLoader().getResourceAsStream("DBCP_Config.properties");
                Properties properties = new Properties();
                assert inputs != null;
                properties.load(inputs);
                datasource = BasicDataSourceFactory.createDataSource(properties);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        public static void free(Connection conn, Statement st, ResultSet res) {
            try {
                if (res != null) //原则1:晚点连接早点释放。原则2:先创建的后释放
                    res.close();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (st != null)
                        st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                } finally {
                    if (conn != null)
                        try {
                            conn.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                }
            }
        }
        public static DataSource getDatasource() {
            return datasource;
        }
    }
    
    

    三、基于ORM表映射,利用数据库连接池设计对应的DAOImpl实现CRUD

    先利用SpringJDBCTemplate返回被封装的JDBC,类型是带命名参数的JDBCTemplate

    package DatabasesOperation.DAOImplSpring;
    
    import DatabasesOperation.JDBCUtils.JDBCUtils;
    import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
    
    /**
     * 使用SpringTemplate实现各个表ORM对象的CRUD;
     * 这个类主要返回一个注册了数据源的命名参数Spring-template 即:nJdbc;
     */
    public class DAOImplSpringTemplate {
        private static NamedParameterJdbcTemplate nJdbc;
    
        public static NamedParameterJdbcTemplate getJDBC() {
            if (nJdbc == null)//多线程安全问题
                synchronized (DAOImplSpringTemplate.class) {
                    if (nJdbc == null)
                        nJdbc = new NamedParameterJdbcTemplate(JDBCUtils.getDatasource());
    
                }
            return nJdbc;
        }
    
    }
    
    

    创建对应表的DAOImpl,如books的DAOImpl实现CRUD操作:

    package DatabasesOperation.DAO_Design.DAOImpl;
    
    import DatabasesOperation.DAOImplSpring.DAOImplSpringTemplate;
    import DatabasesOperation.DAO_Design.ORM.ORM_Books;
    import DatabasesOperation.JDBCUtils.JDBCUtils;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
    import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
    import org.springframework.jdbc.core.namedparam.SqlParameterSource;
    
    import java.io.*;
    import java.sql.*;
    import java.util.*;
    
    /**
     * 实现CRUD_BOOKS
     */
    public class DAOBooks {
        private NamedParameterJdbcTemplate nJDBC;
        private static String[] book = new String[]{//book的映射,不好维护,应该使用文件读取更新操作。
                "id",
                "name",
                "price",
                "surples",
                "publisher",
                "author",
                "publish",
                "type"
        };
    
        public DAOBooks() {
            nJDBC = DAOImplSpringTemplate.getJDBC();
        }
    
        public void addBooks(ORM_Books book) {//添加书
            if (!isExistId(book.getId())) {//id不存在才允许插入
                String sql =
                        "insert into books(id,name,price,surples,publisher,author,publish,type) Values(:id,:name,:price,:surples,:publisher,:author,:publish,:type)";
                SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(book);
                nJDBC.update(sql, parameterSource);
            } else System.out.println("id=" + book.getId() + "的书已经存在!插入无法完成!");//可以利用弹窗
        }
    
        /**
         * 最终的方法:将文件里面的books信息插入到表中。
         *
         * @param file 含有books内容的文件
         */
        public void addBooksByFile(File file) throws IOException {
            //文件一行代表一本书,属性之间使用#分割,且用对应的字段名问号值
            //如price:400#name:大英###surples:3。。
            //通过文件,将文件里面的关于书的信息导入。属于批量导入
            BufferedReader buff = null;
            try {
                buff = new BufferedReader(new FileReader(file));
                String s;
                List<ORM_Books> books = new ArrayList<>();
                while ((s = buff.readLine()) != null) {
                    String[] res = s.split("#+");
                    ORM_Books books1 = setBooks(res);
                    if (!isExistId(books1.getId()))//不存在才添加
                    {
                        books.add(setBooks(res));
                    }
                }
                SqlParameterSource[] params = new BeanPropertySqlParameterSource[books.size()];
                for (int i = 0; i < books.size(); i++) {
                    params[i] = new BeanPropertySqlParameterSource(books.get(i));
                }
                addBatchBooks(params);
            } finally {
                if (buff != null)
                    try {
                        buff.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
            }
        }
    
        /**
         * 批量操作,将传入的参数源多个,批量插入到表中。
         *
         * @param batchArgs 要插入的参数,book数组
         */
        private void addBatchBooks(SqlParameterSource[] batchArgs) {
            String sql =
                    "insert into books(id,name,price,surples,publisher,author,publish,type) Values(:id,:name,:price,:surples,:publisher,:author,:publish,:type)";
            nJDBC.batchUpdate(sql, batchArgs);
        }
    
        /**
         * 根据读出来的字符集构造book类返回
         *
         * @param res 文件里面读出来的字符集,一次代表一个book
         * @return book
         */
        private ORM_Books setBooks(String[] res) {
            ORM_Books books;
            if (res.length <= book.length)//必须与books里的字段长度一致
            {
                books = new ORM_Books();
                String s;
                int i = 0;
                while (i < res.length && res[i].contains(book[i])) {
                    while (res[i].equals(""))//去除空行
                        i = i + 1;
                    s = res[i].substring(book[i].length() + 1);//将值取出来
                    s = s.replace(" ", "");//去除空格
                    switch (i) {
                        case 0:
                            int id = Integer.parseInt(s);
                            books.setId(id);
                            break;
                        case 1:
                            books.setName(s);
                            break;
                        case 2:
                            float price = Float.parseFloat(s);
                            books.setPrice(price);
                            break;
                        case 3:
                            int surples = Integer.parseInt(s);
                            books.setSurples(surples);
                            break;
                        case 4:
                            books.setPublisher(s);
                            break;
                        case 5:
                            books.setAuthor(s);
                            break;
                        case 6:
                            java.util.Date publish = java.sql.Date.valueOf(s);
                            books.setPublish(publish);
                            break;
                        case 7:
                            books.setType(s);
                        default:
                            break;
    
                    }
                    i++;
                }
                return books;
    
    
            } else throw new RuntimeException("文件的格式不对!");
    
    
        }
    
        public void updateBooks(ORM_Books book) {//根据id找到该书,将其更新
            String sql =
                    "update books set name=:name,price=:price,surples=:surples,publisher=:publisher,author=:author,publish=:publish,type=:type where id=:id";
            SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(book);
            nJDBC.update(sql, parameterSource);
        }
        public void updateBooksBatch(List<ORM_Books> books){
            String sql =
                    "update books set name=:name,price=:price,surples=:surples,publisher=:publisher,author=:author,publish=:publish,type=:type where id=:id";
            SqlParameterSource[] parameterSource = new SqlParameterSource[books.size()];
            int i=0;
            for(ORM_Books books1:books){
                parameterSource[i] = new BeanPropertySqlParameterSource(books1);
                i++;
            }
            nJDBC.batchUpdate(sql, parameterSource);
    
        }
    
        public List<Map<String, Object>> findBooks(String keyword) {//根据关键字查看对应的书
            String sql = "select * from books where name Like :keyword";
            Map<String, String> params = new HashMap<>();
            params.put("keyword", "%" + keyword + "%");
            return nJDBC.queryForList(sql, params);
        }
    
        public static List<ORM_Books> displayBooks(String keyword) {//展示书本,返回list
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            List<ORM_Books> list = new ArrayList<>();
            try {
                conn = JDBCUtils.getConnect();//从连接池里面拿连接,不浪费资源
                String sql;
                if(keyword==null){
                    sql = "select * from books";
                    ps = conn.prepareStatement(sql);
                }else {
                    sql = "select * from books where name like ?";
                    ps = conn.prepareStatement(sql);
                    ps.setString(1,"%"+keyword+"%");
                }
                rs = ps.executeQuery();
                ORM_Books books;
                while (rs.next()) {
                    books = new ORM_Books();
                    books.setId(rs.getInt("id"));
                    books.setName(rs.getString("name"));
                    books.setSurples(rs.getInt("surples"));
                    books.setPrice(rs.getFloat("price"));
                    books.setAuthor(rs.getString("author"));
                    books.setPublisher(rs.getString("publisher"));
                    books.setPublish(rs.getDate("publish"));
                    books.setType(rs.getString("type"));
                    list.add(books);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtils.free(conn, ps, rs);
            }
            return list;
        }
    
        public ORM_Books findBooksByName(String book_name) {//根据书的名字查看对应的书
            String sql = "select * from books where name=:name";
            Map<String, String> params = new HashMap<>();
            params.put("name", book_name);
            /*Incorrect column count: expected 1, actual 8
            同时不仅是jdbcTemplate.queryForList不能这么使用,
            queryForObject同样也不能这么使用,而是应该添加new RowMapper接口才能返回自定义的实体类对象。
             */
            List<ORM_Books> books = nJDBC.query(sql, params, new BeanPropertyRowMapper<>(ORM_Books.class));
            if (books.size() == 0)//不能判断null,因为及时返回的元素个数为0,这个list也不是null
                throw new RuntimeException("不存在这样的书名!");
            return books.remove(0);//返回第一个
    
        }
    
        private boolean isExistId(int id) {//判断id是否存在
            String sql = "select id from books where id=:id";
            Map<String, Integer> params = new HashMap<>();
            params.put("id", id);
            List<ORM_Books> list = nJDBC.query(sql, params, new BeanPropertyRowMapper<>(ORM_Books.class));
            return list.size() != 0;
        }
    
        public void deleteBooks(int id) {//根据书的id来删除书本
            String sql = "delete from books where id=:id";
            Map<String, Integer> params = new HashMap<>();
            params.put("id", id);
            nJDBC.update(sql, params);
    
        }
        public static int maxId(){
            Connection conn = null;
            Statement st = null;
            ResultSet rs = null;
            int id = 0;
            try{
                String sql = "select max(id) from books";
                conn = JDBCUtils.getConnect();
                st = conn.createStatement();
    
                rs = st.executeQuery(sql);
                if(rs.next()){
                    id = rs.getInt("max(id)");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
                JDBCUtils.free(conn,st,rs);
            }
            return id;
        }
    
    }
    
    

    我们把这些表的功能实现放在同一个包里面:
    在这里插入图片描述

    四、测试类,用于测试每个类的功能是否完成

    package DatabasesOperation.Test;
    
    import DatabasesOperation.DAO_Design.DAOImpl.DAOBooks;
    import DatabasesOperation.DAO_Design.DAOImpl.DAOUser;
    import DatabasesOperation.DAO_Design.ORM.ORM_Books;
    import GUIModule.PublishMethodGet.Constant_Size;
    import GUIModule.PublishMethodGet.RepaintJPanel;
    
    import javax.swing.*;
    import java.awt.*;
    import java.io.File;
    import java.io.IOException;
    import java.util.Arrays;
    import java.util.List;
    
    
    /**
     * 测试类,用于测试每个DAO实现类的CRUD是否可以实现。
     * 已经通过测试:books表, user表、Reader表、borrow表
     */
    
    public class TestDAOimpl {
        /*public static void main(String[] args) throws IOException {
            DAOBooks daoBooks = new DAOBooks();
            daoBooks.addBooksByFile(new File("src/main/resources/books.txt"));
    
        }*/
    }
    
    

    把它放到测试包里面,当需要添加新功能时,就在这里进行测试
    在这里插入图片描述

    5、图形界面开发

    一、启动界面StartUi

    在这里插入图片描述

    二、注册界面UserRegister

    在这里插入图片描述
    在这里插入图片描述

    三、登陆界面LogInGUi

    管理员的登陆界面也是这个,但是会添加额外功能

    在这里插入图片描述

    四、读者信息界面ReaderInfo

    以读者身份登陆后显示个人信息,头像动态生成,man则是男头像,woman则是女头像
    在这里插入图片描述

    五、读者借书的信息界面(还书操作、ReturnBooksGui)

    点击借书按钮,如果没有借书记录则不进入还书界面
    在这里插入图片描述
    点击借书按钮,进入馆藏资源借书,然后返回:已借书数动态更新
    在这里插入图片描述
    点击还书按钮:
    在这里插入图片描述
    输入id还书或者全部归还
    在这里插入图片描述
    在这里插入图片描述

    六、图书馆资源一览界面LibrarySources

    在这里插入图片描述
    必须登陆才能进行借书:
    在这里插入图片描述

    七、管理员登陆界面AdminRegister

    在登陆界面登陆提示管理员以后,点击是,会进入管理界面,弹出问候语

    在这里插入图片描述
    这里会有很多操作,功能实现等。采用了菜单栏和菜单、菜单选项、监听对应的菜单,触发不同的功能。
    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
    一些还没写的功能就用:
    在这里插入图片描述

    在这里插入图片描述

    2、如何创建一个Map数组以及其实例化

     Map<String,Integer>[] maps =new Map[res.length];
            for(int k=0;k<res.length;k++){
                //同时对应的书的数量加1
                maps[k] = new HashMap<String, Integer>();
                (maps[k]).put("id1",res[k]);
            }
            nJDBC.batchUpdate(sql2,maps);
    

    在这里插入图片描述

    二、遇到的问题

    1、如何使用下拉选择框

     jComboBox_sex = new JComboBox<>(new String[]{"woman", "man"});//文本选择框,只能选择woman或者man
     String sex = Objects.requireNonNull(jComboBox_sex.getSelectedItem()).toString();
    

    2、如何给JPanel添加背景图片

    千万不要信哪些人说的添加Jlabel,治标不治本的。设置透明毫无美感。可以自己手写一个类来实现,利用了重新绘制的函数。

    package GUIModule.PublishMethodGet;
    
    import javax.swing.*;
    import java.awt.*;
    
    /**
     * 能够设置背景图片,不是通过设置label实现的
     */
    
    public class RepaintJPanel extends JPanel {
        //设置主页背景图片的JPanel类
        ImageIcon icon;
        Image img;
    
        public RepaintJPanel(String path) {
            //  /img/HomeImg.jpg 是存放在你正在编写的项目的bin文件夹下的img文件夹下的一个图片
            icon = new ImageIcon(path);
            img = icon.getImage();
        }
    
        /*private RepaintJPanel(ImageIcon icon) {
            this.icon = icon;
        }*/
    
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            //下面这行是为了背景图片可以跟随窗口自行调整大小,可以自己设置成固定大小
            g.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
        }
    }
    
    
    
    

    3、如何动态修改JTable的值,同时检验数据的合格性,不合格不保存、如何动态删除选择的行、以及如何删除一行同时将对应的操作施加到数据库。

    思路:将数据库的数据读取出来,然后得到了二维数据数据bookDate,给表创建单元格编辑器,在编辑器里可以设置对值的检验。然后在监听对应的表数据,将对应的修改值存起来,选择的行利用主键唯一性存起来。然后利用一个按钮来确认删除.就将存的数据提取处理删除。

    表格编辑器

    package GUIModule.PublishMethodGet;
    
    import javax.swing.*;
    
    /**
     * 自动义单元格编辑器,不允许空格
     */
    public  class MyCellEditor extends DefaultCellEditor {
        private JFrame jFrame;
        public MyCellEditor(JTextField textField, JFrame jFrame) {
            super(textField);
            this.jFrame = jFrame;
        }
    
        @Override
        public boolean stopCellEditing() {
            // 获取当前单元格的编辑器组件
           // Component comp = getComponent();
    
            // 获取当前单元格编辑器输入的值
            Object obj = getCellEditorValue();
    
            // 如果当前单元格编辑器输入的值不是数字,则返回 false(表示数据非法,)
            if (obj == null || obj.toString().equals("")) {
                JOptionPane.showMessageDialog(jFrame, "不允许输入空值", "警告!", JOptionPane.ERROR_MESSAGE);
                return false;//不允许设置,无法保存,不调用super.stopCellEdition
            }
    
            // 合法数据交给父类处理
           return super.stopCellEditing();
        }
    }
    
    
    

    给表格添加该编辑器

        // 创建单元格编辑器,使用文本框作为编辑组件
            MyCellEditor cellEditor = new MyCellEditor
                    (new JTextField("", jTable_book.getColumnModel().getColumn(2).getWidth()), mainJFrame);
    
            // 遍历表格中所有数字列,并设置列单元格的编辑器
            for (int i = 1; i < ORM_Books.columnNames.length; i++) {
                // 根据 列名 获取 表格列
                TableColumn tableColumn = jTable_book.getColumn(ORM_Books.columnNames[i]);
                // 设置 表格列 的 单元格编辑器
                tableColumn.setCellEditor(cellEditor);
            }
    

    对表格更新操作进行监听,然后将数据保存到bookData,但是未保存到数据库。在确认按钮上添加监控,点击,则进行对应事件的处理。

     ///表格监听,编辑表格得到的数据将保存到实际的数据库,保存前会发出提示
            jTable_book.getModel().addTableModelListener(e -> {
    
                int type = e.getType();
                if (type == TableModelEvent.UPDATE)//更新操作触发
                {
                    int row_first = jTable_book.getEditingRow(); //获取改变的行,默认每改变一个格子则弹出一次窗口
                    int col = jTable_book.getEditingColumn();//获取改变的列
                    String newInformation = jTable_book.getValueAt(row_first, col).toString();//获取新行的值,视觉行
                    String id = jTable_book.getValueAt(row_first, 0).toString();//获取id
    
                    alter_row.add(id);//获取id值
                    for (int i = 0; i < bookData.length; i++) {
                        if (bookData[i][0].equals(id)) {
                            bookData[i][col] = newInformation;//先缓存到数据bookdata里面
                            break;
                        }
                    }
                }
            });
    
            jButton_confirm.addMouseListener(new MouseListenerNew(jButton_confirm) {
                @Override
                public void mouseClicked(MouseEvent e) {
                    while (jTable_book.isEditing()) {//注意这个操作,当在编辑行时,如果没有保存数据,点击button,没有下面的代码二无法实现更新
                        jTable_book.getCellEditor(jTable_book.getEditingRow(), jTable_book.getEditingColumn()).stopCellEditing();
                    }
    
                    //确认更新操作,会提示真的更新所有修改到数据库?
                    int value = JOptionPane.showConfirmDialog(jFrame, "所有的修改会保存到对应的数据库,是否继续?", "提示", JOptionPane.YES_NO_OPTION);
                    if (value == JOptionPane.YES_OPTION) {
                        DAOBooks daoBooks = new DAOBooks();
                        List<ORM_Books> list = new ArrayList<>();
                        JOptionPane.showMessageDialog(jFrame, "修改中...,请稍等", "提示", JOptionPane.INFORMATION_MESSAGE);
                        for (int i = 0; i < alter_row.size(); i++) {
                            String s = alter_row.remove(i);//获取被修改的书的id
                            //  System.out.println("移除的id____"+s);
                            for (String[] bookDatum : bookData) {
                                if (bookDatum[0].equals(s)) {//找到该id
                                    ORM_Books books = new ORM_Books();
                                    books.setId(Integer.parseInt(s));//设置id,根据id来修改的
                                    books.setName(bookDatum[1]);
                                    books.setAuthor(bookDatum[2]);
                                    books.setPrice(Float.parseFloat(bookDatum[3].trim()));
                                    books.setPublisher(bookDatum[4]);
                                    books.setPublish(Date.valueOf(bookDatum[5]));
                                    books.setType(bookDatum[6]);
                                    books.setSurples(Integer.parseInt(bookDatum[7].trim()));
                                    //alter_row.remove(i);//修改了,则需要在记录数组中移除
                                    list.add(books);
                                }
                            }
                        }
                        if (list.size() == 1)//一个的时候不需要
                            daoBooks.updateBooks(list.get(0));
                        else daoBooks.updateBooksBatch(list);//批量更新
    
                        JOptionPane.showMessageDialog(jFrame, "修改成功!", "提示", JOptionPane.INFORMATION_MESSAGE);
    
                    }
                }
    
    
            });
    

    删除多选的行操作,因为你移除了一次行,对应的行的索引会变化,你要像我这么做:

      jButton_delete.addMouseListener(new MouseListenerNew(jButton_delete) {
                @Override
                public void mouseClicked(MouseEvent e) {
                    while (jTable_book.isEditing()) {//注意这个操作,当在编辑行时,如果没有保存数据,点击button,没有下面的代码二无法实现更新
                        jTable_book.getCellEditor(jTable_book.getEditingRow(), jTable_book.getEditingColumn()).stopCellEditing();
                    }
                    int numLength = jTable_book.getSelectedRows().length;//获取选择的行数
                    if (numLength > 0) {
                        int value = JOptionPane.showConfirmDialog(jFrame, "真的要删除选择图书?",
                                "提示", JOptionPane.YES_NO_OPTION);
                        if (value == JOptionPane.YES_OPTION) {
                            DAOBooks daoBooks = new DAOBooks();
                            for (int i = 0; i < numLength; i++) {
                                int row = jTable_book.getSelectedRow();//因为一行一行的删除会改变行索引,导致我们无法
                                //通过直接导入选择的所有行进行 一行行删除实现
                                int id = Integer.parseInt(jTable_book.getValueAt(row, 0).toString());
                                daoBooks.deleteBooks(id);
                                model.removeRow(row);//移除行后行号变了,所以对应的res[i]又变了。
                            }
                        }
                    } else JOptionPane.showMessageDialog(jFrame, "你还没有选择要删除的行数据!", "警告", JOptionPane.ERROR_MESSAGE);
    
                }
            });
    

    全部删除;

     while(model.getRowCount()>0){//批量删除
                                model.removeRow(0);//一行一行的删除
                            }
    

    表格的排序和过滤:
    TableModel没有移除行model.removeRow(0);//一行一行的删除
    所以我们要将TableModel定义为DefaultTableModel

    TableModel model = new DefaultTableModel(rows, columns);
    JTable table = new JTable(model);
    RowSorter sorter = new TableRowSorter(model);
    table.setRowSorter(sorter);
    

    4、菜单栏和菜单、菜单选项的使用

    先添加菜单栏,再添加菜单,必须设置名字才能看见。。。
    然后在对应的菜单添加多个菜单选项或者子菜单

      JMenuBar jMenuBar = new JMenuBar();//创建菜单栏
            jMenuBar.setForeground(Color.BLUE);
    
            //第一个菜单——用户管理(用户列表,修改用户、删除用户、禁止用户借书、生成用户罚单)
            JMenu jMenu = new JMenu("用户管理");
            //第二个菜单——图书管理(更新图书信息、删除图书(设置余量为0)、 导入图书,批量导入图书)
            JMenu jMenu1 = new JMenu("图书管理");
            //第三个菜单——系统管理(修改用户名和密码,数据库系统重启,数据库系统退出,数据库备份,数据库日志记录,暂停用户登陆、清空数据库)
            JMenu jMenu2 = new JMenu("系统管理");
            //第四个菜单——
            JMenu jMenu3 = new JMenu("退出管理");
    
            //设置菜单1
            userItem1 = new JMenuItem("用户列表");
            userItem2 = new JMenuItem("修改用户");
            userItem3 = new JMenuItem("删除用户");
            userItem4 = new JMenuItem("禁止用户借书");
            userItem5 = new JMenuItem("生成用户罚单");
            jMenu.add(userItem1);
            jMenu.add(userItem2);
            jMenu.add(userItem3);
            jMenu.add(userItem4);
            jMenu.add(userItem5);
    
    

    5、父窗体、子窗体、A-B-C-A等的调用关系如何处理

    利用了传入参数的办法,判断是否null等、或者按照需要将对应的窗体设置为:

     mainJFrame.setDefaultCloseOperation(jFrame.EXIT_ON_CLOSE);//退出所有界面,相当于退出虚拟机
     //DISPOSE_ON_CLOSE退出当前窗体
     public void setDefaultCloseOperation(int operation) {
            if (operation != DO_NOTHING_ON_CLOSE &&
                operation != HIDE_ON_CLOSE &&
                operation != DISPOSE_ON_CLOSE &&
                operation != EXIT_ON_CLOSE) {
                throw new IllegalArgumentException("defaultCloseOperation must be"
                        + " one of: DO_NOTHING_ON_CLOSE, HIDE_ON_CLOSE,"
                        + " DISPOSE_ON_CLOSE, or EXIT_ON_CLOSE");
            }
    

    6、将一些类和方法抽取公共类、公共方法、接口,代码依然冗杂

    在这里插入图片描述

    7、IDEA里面配置MySQL数据库

    在这里插入图片描述

    8、各种布局管理器

    在这里插入图片描述
    一个很实在的讲解:
    运用 BoxLayout 进行 Swing 控件布局

    8、整体项目架构使用maven项目管理工具

    在这里插入图片描述

    三、结语

    项目做了6天,也是自己的第一个项目,完全独立的做,遇到了很多的难题,但是解决了以后现在我想不起啦 。。。总的来说还行。
    贴上总代码行数:
    在这里插入图片描述

    最大的问题:怎么样让代码,不那么冗杂!!!DAO三层架构设计不知道我的到底算不算是

    四、源码

    github:
    https://github.com/linj21a/BooksMangementSystem

    结后语
    说明文档什么的就不会写了。。。。博客匆忙总结一把了。还有好多东西要学,大家加油!不懂就要问,我也不懂😂。大家开始吐槽吧!!!

    展开全文
  • php开发表白墙 |源码|微信表白|微信表白墙|吐槽墙|心愿墙|网站留言板源码告白墙程序使用说明下载之后,文件目录如下:Forlove :程序核心文件夹;Public :样式图片 JS 文件夹,logo图片文件在这里面;ThinkPHP :...
  • 吐槽微博SDK的接入

    2019-07-08 14:52:43
    个人信息没有啊,只有基本昵称与uid,没有头像url等其他信息,如何获取呢,微博开放平台在线API是指望不上了,一堆的未封装的网络请求,只有在这里找吧 微博Android平台SDK文档V3.1.2.docx 找到了,大功告成,累死...
  • 今日罗永浩在微博再发长文,称自己写文章谴责某投资机构耍流氓后,突然冒出很多“专业人士”发表意见,并吐槽道:“突然发现成为‘专业人士’,甚至是没有智力门槛的。近期,罗永浩又一次被法院强制执行...
  • 不过吐槽吐槽,这个“拍一拍”功能还是被网友们玩坏了。 有人忍不住在群里拍了拍老板,成功的得到了被移除群聊的机会。 有人顺手改备注,群聊变得激情四射。 还有人拍后脑勺,拍脑袋聪明死了,拍素颜结果吓死了。...
  • 接入案例

    2019-06-15 17:32:19
    接入文档 网页案例 WEB接入 想要在您的产品中接入,首先需要获取该产品的url,一般由https://support.qq.com/product/ + 该产品的 id组成。 产品的 id 在创建时已经分配给您。每个产品有唯一的 id,...
  • 吐槽CSDN的BUG

    2017-06-19 22:35:56
    遇到一个记一个 在公司写了一半的文章,...这还可以接受,关键无论过去多少天,只要我不清缓存,这个头像就一直显示我以前的,这个问题这是无语。 技术问答里面,凡是有酬劳C币的都会出现两条一模一样的记录。
  • 谁能看明白这幅Java、PHP、C、Ruby语言相互吐槽的搞笑图片都说的是什么? 看到这篇有趣的图片研究了很久也没完全理解各个图片的意思。今天把它贴出来,希望大家能帮我理解一下,各抒己见。下面这个图表中是我...
  • 但是反过头来大家又要想想,系统永远都无法知道你掉线的真正原因是什么,你想要他们酌情处理是不可能的,玩家必然会出现不满的情况,但是最好还是做到多数觉得还行,像这样84%的吐槽就不合适啊!大家觉得如今的这个...
  • DayDayUp:2019.01.24新东方年会—俞敏洪点赞并奖励员工的吐槽神曲《释放自我》12万元!—附全歌词 目录 新东方年会神曲亮了 官微回应:放手去做,未来可期! 各家吃瓜 《释放自我》全歌词 视频链接...
  • 本程序实现的功能是: 头像头像使用了QQ头像取消了之前的随机显示头像,新增了填写QQ的字段,头像会自动获取用户的QQ号码头像; 告白:点击“我要表白”按钮,输入邮箱和昵称等信息,可以进行很少告白;告白后,...
  • [color=#FF0000](已修复)[/color] ...18.论坛原来头像下显示ID,现在只显示昵称,版主管理时还要点昵称在新打开的页面上复制地址栏中的ID才能进行下一步管理,非常繁琐。[color=#FF0000](未修复)[/color]
  • 最近一段时间,感觉我的博客打开速度很慢,页面总是加载不完,仔细检查发现gravatar头像不显示了,再一搜才知道原来gravatar的头像服务器被那啥了,对于隔三差五出现这种情况,相信各位已经无力吐槽,只能在心里默念...
  • 如果你想在Snow上与好友聊天,你会发现好友列表中的头像都是圆形的,这项设计跟Snapchat中的设计也是一样的。 虽然Snapchat的设计优于Snow,功能也比后者更直观,但这并不能阻止Snow在亚洲市场如火如荼地发展壮大...
  • 不过吐槽吐槽,这个“拍一拍”功能还是被网友们玩坏了。 有人忍不住在群里拍了拍老板,成功的得到了被移除群聊的机会。 有人顺手改备注,群聊变得激情四射。 最有意思的是这张图了,压轴!!! 尽管"拍一拍"被...
  • 我想大家对头像上传功能在陌生不过了把,每个人都应该用过,当然,从不更改...难得吐槽一下,不知道是我人品问题还是没用对。如图: 准备工作好了,废话不多说了,一般常用的头像上传有两种(据我所知):普通的文件...
  • 刚做了个人中心的上传头像功能,就来总结一下做的过程。一开始就直接去github上找一个第三方框架接入,省点事,先是听了朋友的推荐去看了一下bilibili开源的一款Boxing的框架,但是...(不得不吐槽一下,这个项目git...
  • 无力吐槽啊···· 11 如图所示操作 12 先选择一个分辨率,在选择下一步 13 非常喜欢PICSART也带有图层功能 14 如图所示操作 15 隐藏 图层功能表 16 添加之前制作的圆形图, 17 如图所示操作 18 选择贴纸列表里面的...
  •  额,貌似废话有点多,开始讲重点吧,这篇博客是在开发的过程中一些比较常见的bug了,或者说是微信设计不合理的地方,我相信有很多人都想吐槽下微信,但我一直没在园子里看到相关吐槽的博客,鄙人不才,献丑了。...
  • 就是吐槽创业好难。自己能力不足,基本上我获得的经验都是segmentfault上面一个个提问得来的。在这里,我要感谢哪些帮助过我的人,不管是多么幼稚的问题都会有人热心的回答你。当然,我又有了新问题。就是陌陌的八张...
  • Social Suite(可制作头像和共享空间的工具包) Screens(用于在房间中一次弹出多个显示并可投屏) Create(Magic Leap Studio的绘画/艺术工具) Tonandi(Sigur Ros’音乐AR应用程序) Magic Leap World应用商店中...
  • vb吐槽班完结篇 L

    千次阅读 热门讨论 2013-12-02 21:53:40
     说的也不少了吧,虽说是完结了,但是说不定哪天我心情好就又写个“吐槽班R2”“吐槽班Z”“吐槽班SeeD”“吐槽班Zero”什么的,偶尔的偷懒还是就当是对自己的奖励好了。反正像我这样无节操,无廉耻,无下限的三无...
  • 总裁的助理对她们说,中午的时候两个人都不在,很多人来送快递和盒饭,两个人至少留一个在前台吧,我要吐槽的那个前台就和她吵起来了 技术总监他们像顽皮的小学生一样,天天看到办公室里打打闹闹的影子,用脚的也...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,999
精华内容 799
关键字:

吐槽头像