Java系统开发从入门到精通第三讲(文字版)
ccwgpt 2025-01-03 14:38 32 浏览 0 评论
下来我们进入数据持久化的部分,对于一个真实的业务系统,能够正常的运转离不开数据的持久化。在数据持久化这块,目前主流的还是关系型数据库(RDBMS),NoSQL(NewSQL)也有了长足发展,特别在大数据领域。
JDBC
数据持久化这块,javaEE推出了JDBC规范,基于JDBC规范,只要不同的数据库支持对应协议,业务系统就能和数据库服务器(MySQL、Oracle、DB2等)交互
JPA
JPA(Java Persistence API)用于对象持久化的 API,是 Java EE 5.0 平台标准的 ORM 规范,使得应用程序以统一的方式访问持久层。
Hibernate 是符合 JPA 规范的,而 MyBatis 却不符合,因为 MyBatis 还是需要写 SQL 的
MyBatis
MyBatis是一个不屏蔽SQL且提供动态SQL、接口式编程和简易SQL绑定POJO的半自动化框架,它的使用十分简单,而且能非常容易定制SQL,以提高网站性能,因此在移动互联网兴起的时代,它占据了强势的地位。
MyBatis的基本概念:数据库表对应的java对象(POJO)、Mapper(映射器,一些绑定映射语句的接口)、映射语句(XML和注解两种方式,过去XML方式更为流行)、Mybatis配置
- 数据表结构
CREATE TABLE `tz_user` (
`user_id` varchar(36) NOT NULL DEFAULT '' COMMENT 'ID',
`nick_name` varchar(50) DEFAULT NULL COMMENT '用户昵称',
`real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`user_mail` varchar(100) DEFAULT NULL COMMENT '用户邮箱',
`login_password` varchar(255) DEFAULT NULL COMMENT '登录密码',
`pay_password` varchar(50) DEFAULT NULL COMMENT '支付密码',
`user_mobile` varchar(50) DEFAULT NULL COMMENT '手机号码',
`modify_time` datetime NOT NULL COMMENT '修改时间',
`user_regtime` datetime NOT NULL COMMENT '注册时间',
`user_regip` varchar(50) DEFAULT NULL COMMENT '注册IP',
`user_lasttime` datetime DEFAULT NULL COMMENT '最后登录时间',
`user_lastip` varchar(50) DEFAULT NULL COMMENT '最后登录IP',
`user_memo` varchar(500) DEFAULT NULL COMMENT '备注',
`sex` char(1) DEFAULT 'M' COMMENT 'M(男) or F(女)',
`birth_date` char(10) DEFAULT NULL COMMENT '例如:2009-11-27',
`pic` varchar(255) DEFAULT NULL COMMENT '头像图片路径',
`status` int(1) NOT NULL DEFAULT '1' COMMENT '状态 1 正常 0 无效',
`score` int(11) DEFAULT NULL COMMENT '用户积分',
PRIMARY KEY (`user_id`),
UNIQUE KEY `ud_user_mail` (`user_mail`),
UNIQUE KEY `ud_user_unique_mobile` (`user_mobile`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';
- 实体类(示例)
@Data
public class User implements Serializable {
private String userId;//id
private String realName;//用户名
private String loginPassword;//密码
}
这是实体对象,和数据表字段一一对应(并且属性名称和数据表字段名称存在规则映射,例如:user_id 为 userId)
这里也体现了”约定“的威力,基于约定很多事情处理都变得异常简单
- Mapper XML
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.easycloud.daomall.show.dao.UserMapper">
<resultMap id="userMap" type="User">
<id property="userId" column="user_id"></id>
<result property="realName" column="real_name" />
<result property="loginPassword" column="login_password" />
</resultMap>
<select id="findAll" resultMap="userMap">
SELECT * FROM tz_user
</select>
</mapper>
这是Mybatis中的核心:
因为SQL命名规范(多以”_“连接)和java(多以驼峰)命名规范不一致,所以通过resultMap标签来做java属性和数据表字段的对应关系;
关于映射,Mybatis提供了三种处理方式:
1、如上代码所示,手动指定(麻烦);
2、SQL语句设置别名(稍显麻烦),例如:SELECT user_id as userId, real_name as realName
3、设置开启驼峰命名map-underscore-to-camel-case: true(基于“约定”的处理);
select标签中的resultMap属性,描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素,它和resultMap标签中的id属性保持一致;
sql标签,可被其它语句引用的可重用语句块; insert标签,映射插入语句; update标签,映射更新语句; delete标签,映射删除语句; select标签,映射查询语句;
对应SQL的CRUD
- Mapper接口
@Mapper
public interface UserMapper {
public List<User> findAll();
}
- MyBatis配置
- 使用Spring Boot,Mybatis配置非常简单
- #mybatis的相关配置
mybatis:
#mapper配置文件
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.easycloud.daomall.show.model
#开启驼峰命名
configuration:
map-underscore-to-camel-case: true - 其中type-aliases-package指定实体类所在的包,这样在设置”type、parameterType、resultType“属性值时可只用类名。
- api暴露
然后按照前面讲的Spring RESTful service暴露,就可以查看访问数据库的效果了
MyBatis-Plus
https://baomidou.com
在实际的业务编写中,你会发现要写大量的CRUD Mapper XML和对应的接口定义,但其实对于数据表的操作基本可以抽象为CRUD(包括分页和高级检索)这几种类型,MyBatis-Plus就应运而生。
使用MyBatis-Plus后,我们看看可以省略哪些代码:
- application.yml中的MyBatis配置
- Mapper XML文件
- Mapper java接口中的方法定义,然后继承BaseMapper
- public interface UserMapper extends BaseMapper<User> {
- 内置方法说明参见:https://baomidou.com/pages/49cc81/
- 调用Mapper的地方改为selectList(null)
- 实体类名和数据表名不存在规则一致性的话,需要加上TableName注解(例如我们的代码示例)
- @Data
@TableName("tz_user")
public class User implements Serializable { - MyBatis-Plus处处体现了”约定优于配置“的原则
分页实现
分页插件,在实际的业务中,分页是一个常见的场景,使用MyBatis-Plus分页,需要启用分分页插件
@Configuration
public class MyBatisConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
分页代码
IPage<User> page = new Page<User>(start, rows);
return userMapper.selectPage(page,null).getRecords();
其中Page对象,第一个参数表示是第几页,第二个参数表示每页有几条数据
条件构造
https://baomidou.com/pages/10c804/
MyBatis-Plus中使用Wrapper实现,支持查询、更新的where条件。使用Lambda 表达式更加的简洁。
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(User::getUserId, "111");
上面就是查询userId为111的用户
其它特性
作为一个可扩展的设计,数据表ID不应该使用数据库自增ID(例如:分库分表就会出现ID重复问题),所以MyBatis-Plus提供了ID生成的增强
- 注解使用ID生成策略
@TableId()
private String userId;
不做任何设置,使用其默认内置的雪花算法
它还支持其他几种模式,非必要使用默认方式足矣
ASSIGN_ID(雪花算法) 如果不设置类型值,默认则使用IdType.ASSIGN_ID策略(自3.3.0起)。该策略会使用雪花算法自动生成主键ID,主键类型为长或字符串(分别对应的MySQL的表字段为BIGINT和VARCHAR)
ASSIGN_UUID(排除中划线的UUID) 如果使用IdType.ASSIGN_UUID策略,并重新自动生成排除中划线的UUID作为主键。主键类型为String,对应MySQL的表分段为VARCHAR(32)
AUTO(数据库ID自增)
INPUT(插入前自行设置主键值)
无(无状态) 如果使用IdType.NONE策略,表示未设置主键类型(注解里等于跟随上下,左右里约等于INPUT)
策略全局设置
mybatis-plus.global-config.db-config.id-type=值
- 调用
User user = new User();
user.setUserRegtime(LocalDateTime.now());
user.setModifyTime(LocalDateTime.now());
userService.save(user);
其他前置知识
Java
Lambda
我们知道java8是一个大改动的版本,也让java的能力有了更进一步的提升,并且很多新特性应用广泛,Lambda表达式就是其中之一,在MyBatis章节我们就有使用Lambda表达式,第一感觉就是书写更加的方便,非常的强大。下来我们就对Lambda表达式做一定的了解。
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
Lambda 表达式的语法格式如下:
(parameters) -> expression
或
(parameters) ->{ statements; }
Lambda 表达式的简单例子:
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
在 LambdaTester.java 文件输入以下代码:
package com.easycloud.javacase.lambda;
public class LambdaShow {
public static void main(String args[]){
LambdaShow tester = new LambdaShow();
// 类型声明
MathOperation addition = (int a, int b) -> a + b;
// 不用类型声明
MathOperation subtraction = (a, b) -> a - b;
// 大括号中的返回语句
MathOperation multiplication = (int a, int b) -> { return a * b; };
// 没有大括号及返回语句
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// 不用括号
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
// 用括号
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("Runoob");
greetService2.sayMessage("Google");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
}
使用 Lambda 表达式需要注意以下两点:
- Lambda 表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,我们使用各种类型的Lambda表达式来定义MathOperation接口的方法。然后我们定义了sayMessage的执行。
- Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。
我们来看一下,引入Lambda表达式对以前以前繁琐写法的改进,
例如常见的排序算法:
java8以前:
// 使用 java 7 排序
private void sortUsingJava7(List<String> names){
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
}
java8的Lambda:
// 使用 java 8 排序
private void sortUsingJava8(List<String> names){
Collections.sort(names, (s1, s2) -> s1.compareTo(s2));
}
再例如(多线程编程):
java8以前:
Runnable r2 = new Runnable(){
public void run(){
System.out.println("Hello World 2");
}
};
//执行Runnable方法
public static void process(Runnable r){
r.run();
}
//打印 "Hello World 1"
process(r1);
//打印 "Hello World 2"
process(r2);
java8的Lambda:
Runnable r1 = () -> System.out.println("Hello World 1");
Runnable r2 = () -> System.out.println("Hello World 2");
另外我们在Mybatis的QueryWrapper中使用了”方法引用“,用一对冒号::代表方法引用,使语言调用更加紧凑简洁,减少冗余代码。
queryWrapper.lambda().eq(User::getUserId, "111")
和Lambda配合使用的场景很多,上面演示的接口sayMessage()和Runnable接口,都是函数式接口(Functional Interface)
函数式接口
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 lambda 表达式。
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。
JDK 1.8 之前已有的函数式接口:
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.util.Comparator
- java.io.FileFilter
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
JDK 1.8 新增加的函数接口:
- java.util.function
我们再来看一个具体的示例:
Predicate <T> 接口是一个函数式接口,它接受一个输入参数 T,返回一个布尔值结果。
该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)。
该接口用于测试对象是 true 或 false。
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Java8Tester {
public static void main(String args[]){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// Predicate<Integer> predicate = n -> true
// n 是一个参数传递到 Predicate 接口的 test 方法
// n 如果存在则 test 方法返回 true
System.out.println("输出所有数据:");
// 传递参数 n
eval(list, n->true);
// Predicate<Integer> predicate1 = n -> n%2 == 0
// n 是一个参数传递到 Predicate 接口的 test 方法
// 如果 n%2 为 0 test 方法返回 true
System.out.println("输出所有偶数:");
eval(list, n-> n%2 == 0 );
// Predicate<Integer> predicate2 = n -> n > 3
// n 是一个参数传递到 Predicate 接口的 test 方法
// 如果 n 大于 3 test 方法返回 true
System.out.println("输出大于 3 的所有数字:");
eval(list, n-> n > 3 );
}
public static void eval(List<Integer> list, Predicate<Integer> predicate) {
for(Integer n: list) {
if(predicate.test(n)) {
System.out.println(n + " ");
}
}
}
}
相关推荐
- 自己动手写Android数据库框架_android开发数据库搭建
-
http://blog.csdn.net/feiduclear_up/article/details/50557590推荐理由关于Android数据库操作,由于每次都要自己写数据库操作,每次还得去...
- 谷歌开源大模型评测工具LMEval,打通谷歌、OpenAI、Anthropic
-
智东西编译|金碧辉编辑|程茜智东西5月28日消息,据科技媒体TheDecoder5月26日报道,当天,谷歌正式发布开源大模型评测框架LMEval,支持对GPT-4o、Claude3.7...
- 工信部:着力推动大模型算法、框架等基础性原创性的技术突破
-
工信部新闻发言人今日在发布会上表示,下一步,我们将坚持突出重点领域,大力推动制造业数字化转型,推动人工智能创新应用。主要从以下四个方面着力。一是夯实人工智能技术底座。通过科技创新重大项目,着力推动大模...
- 乒乓反复纠结“框架不稳定”的三个小误区
-
很多球友由于对框架的认知不清晰,往往会把“框架不稳定”当成一种心理负担,从而影响学球进度,其典型状态就是训练中有模有样,一旦进入实战,就像被捆住了手脚。通过训练和学习,结合“基本功打卡群”球友们交流发...
- 前AMD、英特尔显卡架构师Raja再战GPU,号称要全面重构堆栈
-
IT之家8月5日消息,知名GPU架构师拉贾科杜里(RajaKoduri)此前曾先后在AMD和英特尔的显卡部门担任要职。而在今日,由Raja创立的GPU软件与IP初创企...
- 三种必须掌握的嵌入式开发程序架构
-
前言在嵌入式软件开发,包括单片机开发中,软件架构对于开发人员是一个必须认真考虑的问题。软件架构对于系统整体的稳定性和可靠性是非常重要的,一个合适的软件架构不仅结构清晰,并且便于开发。我相...
- 怪不得别人3秒就知道软考案例怎么做能50+
-
软考高级统一合格标准必须三科都达到45分,案例分析也一直是考生头疼的一门,但是掌握到得分点,案例能不能50+还不是你们说了算吗?今天就结合架构案例考点,分享实用的备考攻略~一、吃透考点,搭建知识框架从...
- UML统一建模常用图有哪些,各自的作用是什么?一篇文章彻底讲透
-
10万+爆款解析:9大UML图实战案例,小白也能秒懂!为什么需要UML?UML(统一建模语言)是软件开发的“蓝图”,用图形化语言描述系统结构、行为和交互,让复杂需求一目了然。它能:降低沟通成本避...
- 勒索软件转向云原生架构,直指备份基础设施
-
勒索软件组织和其他网络犯罪分子正越来越多地将目标对准基于云的备份系统,对久已确立的灾难恢复方法构成了挑战。谷歌安全研究人员在一份关于云安全威胁演变的报告中警告称,随着攻击者不断改进数据窃取、身份泄露和...
- ConceptDraw DIAGRAM:释放创意,绘就高效办公新未来
-
在当今数字化时代,可视化工具已成为提升工作效率和激发创意的关键。ConceptDrawDIAGRAM,作为一款世界顶级的商业绘图软件,凭借其强大的功能和用户友好的界面,正逐渐成为众多专业人士的首选绘...
- APP 制作界面设计教程:一步到位_app界面设计模板一套
-
想让APP界面设计高效落地,无需繁琐流程,掌握“框架搭建—细节填充—体验优化”三步法,即可一步到位完成专业级设计。黄金框架搭建是基础。采用“三三制布局”:将屏幕横向三等分,纵向保留三...
- MCP 的工作原理:关键组件_mcp部件
-
以下是MCP架构的关键组件:MCP主机:像ClaudeDesktop、GitHubCopilot或旅行助手这样的AI智能体,它们希望通过MCP协议访问工具、资源等。MCP主机会...
- 软件架构_软件架构师工资一般多少
-
软件架构师自身需要是程序员,并且必须一直坚持做一线程序员。软件架构应该是能力最强的一群程序员,他们通常会在自身承接编程任务的同时,逐渐引导整个团队向一个能够最大化生产力的系统设计方向前进。软件系统的架...
- 不知不觉将手机字体调大!老花眼是因为“老了吗”?
-
现在不管是联系、交友,还是购物,都离不开手机。中老年人使用手机的时间也在逐渐加长,刷抖音、看短视频、发朋友圈……看手机的同时,人们也不得不面对“视力危机”——老花眼,习惯眯眼看、凑近看、瞪眼看,不少人...
- 8000通用汉字学习系列讲座(第046讲)
-
[表声母字]加(续)[从声汉字]伽茄泇迦枷痂袈笳嘉驾架咖贺瘸(计14字)嘉[正音]标准音读jiā。[辨形]上下结构,十四画。会意形声字,从壴从加,加也表声。注:从壴,字义与鼓乐有关;从加,字义与...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 框架图 (58)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- java日志框架 (61)
- mfc框架 (52)
- abb框架断路器 (48)
- beego框架 (52)
- java框架spring (58)
- grpc框架 (65)
- tornado框架 (48)
- 前端框架bootstrap (54)
- orm框架有哪些 (51)
- 知识框架图 (52)
- ppt框架 (55)
- 框架图模板 (59)
- 内联框架 (52)
- cad怎么画框架 (58)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)