百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

Java系统开发从入门到精通第三讲(文字版)

yuyutoo 2025-02-26 14:26 9 浏览 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



    
        
        
        
    
    

这是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 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 page = new Page(start, rows);
return userMapper.selectPage(page,null).getRecords();

其中Page对象,第一个参数表示是第几页,第二个参数表示每页有几条数据

条件构造

https://baomidou.com/pages/10c804/

MyBatis-Plus中使用Wrapper实现,支持查询、更新的where条件。使用Lambda 表达式更加的简洁。

QueryWrapper 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 names){   
      Collections.sort(names, new Comparator() {
         @Override
         public int compare(String s1, String s2) {
            return s1.compareTo(s2);
         }
      });
   }

java8的Lambda:

// 使用 java 8 排序
   private void sortUsingJava8(List 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,返回一个布尔值结果。

该接口包含多种默认方法来将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 list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        
      // Predicate predicate = n -> true
      // n 是一个参数传递到 Predicate 接口的 test 方法
      // n 如果存在则 test 方法返回 true
        
      System.out.println("输出所有数据:");
        
      // 传递参数 n
      eval(list, n->true);
        
      // Predicate predicate1 = n -> n%2 == 0
      // n 是一个参数传递到 Predicate 接口的 test 方法
      // 如果 n%2 为 0 test 方法返回 true
        
      System.out.println("输出所有偶数:");
      eval(list, n-> n%2 == 0 );
        
      // Predicate 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 list, Predicate predicate) {
      for(Integer n: list) {
        
         if(predicate.test(n)) {
            System.out.println(n + " ");
         }
      }
   }
}

相关推荐

自卑的人容易患抑郁症吗?(自卑会导致抑郁吗)

Filephoto[Photo/IC]Lowself-esteemmakesusfeelbadaboutourselves.Butdidyouknowthatovert...

中考典型同(近)义词组(同义词考题)

中考典型同(近)义词组...

WPF 消息传递简明教程(wpf messagebox.show)

...

BroadcastReceiver的原理和使用(broadcast-suppression)

一、使用中注意的几点1.动态注册、静态注册的优先级在AndroidManifest.xml中静态注册的receiver比在代码中用registerReceiver动态注册的优先级要低。发送方在send...

Arduino通过串口透传ESP 13板与java程序交互

ESP13---是一个无线板子,配置通过热点通信Arduino通过串口透传ESP13板与java程序交互...

zookeeper的Leader选举源码解析(zookeeper角色选举角色包括)

作者:京东物流梁吉超zookeeper是一个分布式服务框架,主要解决分布式应用中常见的多种数据问题,例如集群管理,状态同步等。为解决这些问题zookeeper需要Leader选举进行保障数据的强一致...

接待外国人英文口语(接待外国友人的英语口语对话)

接待外国人英文口语询问访客身份:  MayIhaveyourname,please?  请问您贵姓?  Whatcompanyareyoufrom?  您是哪个公司的?  Could...

一文深入理解AP架构Nacos注册原理

Nacos简介Nacos是一款阿里巴巴开源用于管理分布式微服务的中间件,能够帮助开发人员快速实现动态服务发现、服务配置、服务元数据及流量管理等。这篇文章主要剖析一下Nacos作为注册中心时其服务注册与...

Android面试宝典之终极大招(android面试及答案)

以下内容来自兆隆IT云学院就业部,根据多年成功就业服务经验,以及职业素养课程部分内容,归纳总结:18.请描述一下Intent和IntentFilter。Android中通过Intent...

除了Crontab,Swoole Timer也可以实现定时任务的

一般的定时器是怎么实现的呢?我总结如下:1.使用Crontab工具,写一个shell脚本,在脚本中调用PHP文件,然后定期执行该脚本;2.ignore_user_abort()和set_time_li...

Spark源码阅读:DataFrame.collect 作业提交流程思维导图

本文分为两个部分:作业提交流程思维导图关键函数列表作业提交流程思维导图...

使用Xamarin和Visual Studio开发Android可穿戴设备应用

搭建开发环境我们需要做的第一件事情是安装必要的工具。因此,你需要首先安装VisualStudio。如果您使用的是VisualStudio2010,2012或2013,那么请确保它是一个专业版本或...

Android开发者必知的5个开源库(android 开发相关源码精编解析)

过去的时间里,Android开发逐步走向成熟,一个个与Android相关的开发工具也层出不穷。不过,在面对各种新鲜事物时,不要忘了那些我们每天使用的大量开源库。在这里,向大家介绍的就是,在这个任劳任怨...

Android事件总线还能怎么玩?(android实现事件处理的步骤)

顾名思义,AndroidEventBus是一个Android平台的事件总线框架,它简化了Activity、Fragment、Service等组件之间的交互,很大程度上降低了它们之间的耦合,使我们的代码...

Android 开发中文引导-应用小部件

应用小部件是可以嵌入其它应用(例如主屏幕)并收到定期更新的微型应用视图。这些视图在用户界面中被叫做小部件,并可以用应用小部件提供者发布。可以容纳其他应用部件的应用组件叫做应用部件的宿主(1)。下面的截...

取消回复欢迎 发表评论: