java 数据库_java数据库 - CSDN
精华内容
参与话题
  • Java数据库的介绍和使用

    千次阅读 2020-08-18 13:49:14
    数据库管理系统(DataBase Management System,DBMS):指一种操作和管理数据库的大型软件,用于建立、使用和维护数据库,对数据库进行统一管理和控制,以保证数据库的安全性和完整性。用户通过数据库管理系统访问...

    数据库

    第一章 介绍

    数据库(DataBase,DB):指长期保存在计算机的存储设备上,按照一定规则组织起来,可以被各种用户或应用共享的数据集合。
    数据库管理系统(DataBase Management System,DBMS):指一种操作和管理数据库的大型软件,用于建立、使用和维护数据库,对数据库进行统一管理和控制,以保证数据库的安全性和完整性。用户通过数据库管理系统访问数据库中的数据。

    第二章:常见数据库管理系统

    • MySQL
      MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件。
    • Orcale
      Oracle数据库被认为是业界目前比较成功的关系型数据库管理系统。Oracle数据库可以运行在UNIX、Windows等主流操作系统平台,完全支持所有的工业标准,并获得最高级别的ISO标准安全性认证。
    • DB2
      DB2是IBM公司的产品,DB2数据库系统采用多进程多线索体系结构,其功能足以满足大中公司的需要,并可灵活地服务于中小型电子商务解决方案。
    • Microsoft SQL Server
      SQL Server 是Microsoft 公司推出的关系型数据库管理系统。具有使用方便可伸缩性好与相关软件集成程度高等优点。

    第三章:SQL语言(Structure Query Language)

    DDL

    Data Definition Language 数据定义语言

    • 作用:用来定义数据库对象:库、表、列等
    /* 操作数据库 */
    --增 create
    create database 数据库名; --创建新的数据库
    create database 数据库名 character set GBK;--创建新的字符集为GBK的数据库
    --查 show
    show databases;--查看DBMS中所有的数据库
    show create database 数据库名;--查看数据库的定义信息
    --改 alter
    alter database 数据库名 character set utf8;--把数据库改成utf8字符集
    --删 drop
    drop database 数据库名;--删除数据库
    --其他
    use 数据库名;--使用数据库
    select database();--查看当前正在使用的数据库
    flush privileges;--刷新数据库
    
    /* 操作表 */
    --增 create
    create table 表名(--创建新表
    字段名1 字段类型(长度) [约束],
    字段名2 字段类型(长度) [约束],
    ...
    );
    --查 show
    show tables;--查询数据库中有的表
    show create table 表名;--查看表格的创建细节
    desc 表名;--查看表的字段信息					
    --改 alter
    alter table 表名 add 列名 数据类型[约束];--添加一个列
    alter table 表名 change 旧列名 新列名 新数据类型[约束];--修改列的列名
    alter table 表名 modify 列名 新数据类型[约束];--修改列的数据类型
    alter table 表名 drop 列名;--删除表的列
    alter table 表名 AUTO_INCREMENT=5;--重新设置自增id
    rename table 旧表名 to 新表名;--修改表名
    alter table 表名 character set 字符集;--修改表的字符集
    --删 drop
    drop table 表名;
    drop table if exists 表名;--如果表存在,则删除;如果不存在,也不会报错.这种语句是MySql特有的语句,其他的数据库都是不支持这种语法的
    

    DML

    Data Manipulation Language 数据操作语言

    • 作用:用来定义数据库记录(数据)
    --增 insert
    insert into 表名 values(字段值1,字段值2, ...);--全字段插入
    insert into 表名(字段1) values(字段值1);--部分字段插入
    insert into 表名 values(记录1的所有字段),(记录2的所有字段)...;--一次性插入多条 数据
    --改 update
    update 表名 set 列名1=列值1,列名2=列值2 ... where 列名=;--修改操作
    update 表名 set 列名1=列值1;--没有where条件的,会直接修改所有列名的值
    update 表名 set 列名1 = replace(列名1,'原内容','要替换的内容');--批量替换字段里的字符串
    --删 delete
    delete from 表名 where 列名=;--删除表中条件是where的
    delete from 表名;--删除表中所有数据,表结构还在,删除后的数据可以找回
    truncate table 表名;--删除表中所有数据,把表直接drop掉,然后再创建一个同样的新表-删除的数据不能找回。可以使用该命令让id重新从1开始自动递增
    

    DQL

    Data Query Language 数据查询语言

    • 作用:用来查询记录(数据)
      查询语句的顺序
      书写顺序:select → from →join on → where → group by → having → order by → limit
      执行顺序:from → join on → where → group by → having → select → order by → limit
      /* 简单查询 */
      select * from emp;--查询所有列
      select empno,ename,job from emp;--查询指定的列
      select empno as '编号',ename as '别名' from emp;--给列起别名(as可以省略可以使用"",''或者不使用引号)
      select e.empno,e.ename from emp as e;--给表起别名,表中的字段也能通过表的别名来调用
      /* 条件查询 */
      --条件语句 >  <  >=  <=  !=(<>)  =
      --and(&&)
      select * from emp where deptno=10 and sal>2000;--可以使用&&带代替
      --or(||)
      select * from emp where ename='smith' or sal=5000;--可以使用||来代替
      --between...and
      select * from emp where ename between 'A' and 'F';--BETWEEN AND作用于字符型的数据的时候,包含第一个边界值,但是不包含第二个边界值
      --in(set)
      select * from emp where sal in(800,1600);--in表示包含的意思
      --is null  (尽量不要使用,会导致索引失效)
      select * from emp where comm is null;--对于null的判断不能使用=号
      --not
      select * from emp where deptno !=10;
      select * from emp where deptno <>10;
      select * from emp where not deptno =10;
      select * from emp where comm is not null;
      select * from emp where not comm is null;
      --注意
      --对于字符型的数据,是严格区分大小写的
      --Oracle在任何的平台上都是严格区分字符型数据大小写的
      --MySql在Liunx/Unix平台上严格区分字符型数据大小写,但是在window平台上不区分字符型数据大小写
      --可以使用BINARY关键字,让MySql在window平台上也严格字符字符型数据的大小写
      select * from emp where binary ename='smith';
      /* 模糊查询 */
      select * from emp where ename like '_M%';-- _ :任意一个字母 查询第二个字母是m的人
      select * from emp where ename like '%m%';-- %:任意0~n个字母 查询名字包含m的人		
      /* 字段控制查询 */		
      select salary*12 as '年薪' from emp;--可以使用算术表达式将月薪以年薪的形式显示
      select distinct deptno from emp;--去掉重复值查询
      select *,sal+ifnull(comm,0) from emp;
      /* 排序 */
      select * from emp order by sal asc;--升序排序,可默认不写
      select * from emp order by sal desc;--降序排序
      select * from emp order by sal desc,empno asc;--按月薪降序排序,如果月薪相同,按编号升序排序
      /* 单行函数 */
      --year(日期字段) 将日期格式化为年(月日时分秒同理)
      select year(now())--输出今年
      select year('2018-01-01');--2018
      --substr(被截取字段名称,起启下标,截取长度) 截取字符串的函数
      select ename,substr(ename,2,1) from emp;
      --length(str) 返回字符型数据的长度
      select ename,length(ename) from emp;
      select * from emp where length(ename)=5;--查询名字的长是5的员工
      --lower(str) 把字符型数据转换为小写的字符
      select ename,lower(ename) from emp;
      --upper(str) 把字符型数据转换为大写的字符
      select * from emp where binary ename=upper('smith');
      /* 聚合函数 */
      --count() 统计指定列不为NULL的记录行数
      select count(*) from emp;
      select count(*) from emp where sal+ifnull(comm,0)>2500
      --max() 计算指定列的最大值,如果指定列是字符串类型,那么使用字符串排序运算
      select max(sal) from emp;
      --min() 计算指定列的最小值,如果指定列是字符串类型,那么使用字符串排序运算
      select min(sal) from emp;
      --sum() 计算指定列的数值和,如果指定列类型不是数值类型,那么计算结果为0
      select sum(sal) from emp;
      --avg() 计算指定列的平均值,如果指定列类型不是数值类型,那么计算结果为0
      select avg(sal) from emp;
      /* 分组查询 */
      --分组查询
      select deptno,sum(sal) from emp group by deptno;
      --HAVING子句
      select deptno,sum(sal) from emp group by deptno having sum(sal)>9000;
      	--having与where的区别:where是先筛选后分组;having是先分组后筛选
      /* LIMIT */
      --分页查询
      select * from emp limit 5;--查询5行记录,默认从0开始
      select * from emp limit 0,5;--查询5行记录,起始行从0开始
      select * from emp limit 3,10;--查询10行记录,起始行从3开始
      	--注意:LIMIT只能在MySql和SqlServer中有效
      /* 多表查询(跨表查询) */
      --联合查询
        --说明:合并结果集就是把两个select语句的查询结果合并到一起,被合并的两个结果:列数、列类型必须相同
        --union 去除重复记录
        --union all 不去除重复记录
      --连接查询
        --内连接
          --等值连接:建立在父子表关系上,用等号来连接两个表
      		select e.empno,e.ename,e.job,e.sal,d.deptno,d.dname
        		from emp e,dept d
        		where e.deptno=d.deptno;--SQL1992语法
        	select e.empno,e.ename,e.job,e.sal,d.deptno,d.dname
        		from emp e
        		inner join dept d on(e.deptno=d.deptno);--SQL1999语法
        	--使用内连接的时候,inner可以忽略
      	--非等值连接:两个表之间没有父子关系,用非等号来连接两个表
      	select e.empno,e.ename,e.sal,s.grade,s.losal,s.hisal
        	from emp e,salgrade s
        	where e.sal between s.losal and s.hisal;--SQL1992语法
        select e.empno,e.ename,e.sal,s.grade,s.losal,s.hisal
        	from emp e
        	join salgrade s on e.sal between s.losal and s.hisal--SQL1999语法
        	--自连接:使用别名将一个表虚拟成两个表(父子表),然后再这两个表上面做等值连接
        	select e.empno '员工编号',e.ename '员工姓名',m.empno '经理编号',m.ename '经理姓名'
        		from emp e,emp m
        		where e.mgr=m.empno && m.empno=7698--SQL1992语法
        	select e.empno '员工编号',e.ename '员工姓名',m.empno '经理编号',m.ename '经理姓名'
        		from emp e
        		join emp m on(e.mgr=m.empno)
        		where m.empno=7698--SQL1999语法
        --外连接
      		--左外连接(LEFT OUTER JOIN):左外连接可以把左表中不满足对应关系的数据查询出来
      		select e.empno,e.ename,e.job,e.sal,d.deptno,d.dname
        		from emp e
        		left outer join dept d on e.deptno=d.deptno;--使用外连接的时候,outer可以忽略
        	select e.empno,e.ename,e.job,e.sal,d.deptno,d.dname
        		from dept d
        		left join emp e on e.deptno=d.deptno;--左表的位置是相对的
        	--右外连接(RIGHT OUTER JOIN):右外连接可以把右表中不满足对应关系的数据查询出来
        	select e.empno,e.ename,e.job,e.sal,d.deptno,d.dname
        		from emp e
        		right join dept d on e.deptno=d.deptno;
        	--全外连接(FULL OUTER JOIN):可以把左右两个表中不满足对应关系的数据都查询出来
        	select e.empno,e.ename,e.job,e.sal,d.deptno,d.dname
        		from emp e
        		full join dept d on e.deptno=d.deptno;--Oracle支持全外连接,MySql不支持全外连接
        	--join的更多用法见下表
      	--子查询
        	  --1.用来给主查询提供查询条件或查询数据而首先执行的一个查询
        	  --2.主查询使用子查询的结果
        	  --子查询必须要放在()里面
        	--分类
        	  --出现在where中的子查询,用来给查询提供查询条件的
        	  select * from emp
        	  where sal>(select max(sal) from emp where deptno=30);--查询高于30部门所有人工资的员工信息
        	  --出现下from后面的子查询,用来给主查询提供查询数据的(all,any)
        	  select * from emp
        	  where sal>all(select sal from emp);--查询高于30部门所有人工资的员工信息
        	  --(了解)出现在查询列表中的子查询,功能类似于外连接的效果
    

    在这里插入图片描述

    常用函数

    /* 数据函数 */
    -- abs() 取绝对值
    select abs(-1); -- 1
    -- round() 用于数据的四舍五入
    select round(13.26723,2);-- 结果13.27
    --rand() 生成随机数
    select rand();--生成一个0到1之间的随机数
    -- ceiling() 向上取整
    select ceiling(2.5); -- 3
    -- floor() 向下取整
    select floor(2.5); -- 2
    -- sign() 符号函数: 负数返回-1,正数返回1,0返回0
    select sign(2);  -- 1
    /* 字符串函数 */
    -- char_length(str) 返回字符串包含的字符数
    select char_length('abcdef'); -- 6
    -- concat() 合并字符串,参数可以有多个
    select concat('我爱','北京','天安门'); -- 我爱北京天安门
    -- insert() 替换字符串,从某个位置开始替换某个长度
    select insert('一二三四五六七',1,3,'八八八'); -- 八八八四五六七
    -- lower() 把字符型数据转换为小写的字符
    select lower('Manaphy'); -- manaphy
    -- upper() 把字符型数据转换为大写的字符
    select upper('Manaphy'); -- MANAPHY
    -- left() 从左边截取字符串
    select left('Manaphy',3); -- Man
    -- right() 从右边截取字符串
    select right('Manaphy',3); -- phy
    -- replace() 替换字符串
    select replace('一二三四五六七','四五','八九'); -- 一二三八九六七
    -- substr(被截取字段名称,起启下标,截取长度) 截取字符串的函数
    select substr('一二三四五六七',1,3); -- 一二三
    -- reverse() 反转字符串
    select reverse('一二三四五六七'); -- 七六五四三二一
    /* 日期函数 */
    -- year(日期字段) 将日期格式化为年(月日时分秒同理)
    select year(now());-- 输出今年
    select year('2018-01-01');-- 2018
    -- 获取当前日期
    select current_date();
    select curdate();
    -- 获取当前日期和时间
    select now();
    select localtime();
    select sysdate();
    -- 获取当前时间
    select curtime();
    select current_time();
    

    DCL

    Data Control Language 数据控制语言

    • 作用:用来定义访问权限和安全级别
      /* 实体完整性 */
      --主键约束 primary key 说明:每个表中要有一个主键, 数据唯一, 且不能为null
      create table stu(
      id int primary key
      );--创建表时指定主键
      
      create table stu(
      id int,
      name varchar(10),
      primary key(id,name)
      );--联合主键,创建了两个列作为主键
      
      create table stu(
      id int,
      name varchar(10)
      );
      alter table stu add primary key(id);
      alter table stu modify id int primary key;--在已经有表的情况下使用
      
      --唯一约束 unique 说明:数据不能重复
      create table stu(
      id int primary key,
      name varchar(50) unique
      );
      
      --自动增长列
        --auto_increment(mysql)
        --identity(sqlserver)
        --sequence(oracle)
        --说明:给主键添加自动增长的数值,列只能是整数类型
      create table stu(
      id int primary key auto_increment
      );--mysql的方式
      
      /* 域完整性 */
      --数据类型
        --数值类型:tinyint(1字节) int(4字节) bigint(8字节) folat(4字节) double(8字节) decimal
        --日期类型
      	date--1000-01-01/9999-12-31
      	datetime--1000-01-01 00:00:00/9999-12-31 23:59:59
      	timestamp--1970-01-01 00:00:00/2038 结束时间是第 2147483647(2^31-1)秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07
      	time--'-838:59:59'/'838:59:59' (±35天)
      	year--1901/2155
        --字符串类型:char(定长字符串) varchar(变长字符串) binary varbinary text(长文本数据) enum set
      --非空约束
      create table stu(
      id int primary key,
      name varchar(10) not null
      );
      --默认值约束
      create table stu(
      id int primary key,
      sex varchar(2) default '男'
      );--默认是男
      insert into stu(id) values(1);--默认是男
      insert into stu values(2,default);--默认是男
      insert unto stu values(3,null);--此处是null
      --check约束
      check(sex='男' or  sex='女')--(mysql不支持)
      /* 引用完整性 */
      --外键约束
      alter table 左表 add foreign key(左表的某个字段) references 右表(主键);
      create table 表名(
      id int primary key,
      name varchar(20),
      classno bigint,
      foreign key(classno) references tb_class(classno);
        --注意
      	--1.从表的列引用了主表的主键,那么该列就称为外键
      	--2.外键的名字可以不相同
      	--3.外键的数据类型必须与主表中主键的数据类型完全相同
      	--4.外键可以是null值
      /* 表与表之间的关系 */
      --1.一对多: 一张表中的一条记录再另一张表中出现多次
      --2.多对多: 要想两张表之间实现多对多的关系,必须有一张中间表,通过中间表创建与两张表的外键约束,实现多个一	对多,使用这种方式创建外键约束,非常适合实现多个一对多。
      --3.一对一:一张表里的记录,在另外一张表里只能出现一次.表和表之间的关系必须通过外键来创建,但是外键一创建就是一对多,因此在从表里将外键设置唯一性约束,实现一对一
      
    
    展开全文
  • Java连接MySQL数据库——含步骤和代码

    万次阅读 多人点赞 2019-06-01 23:59:22
     MySQL连接驱动:mysql-connector-java-5.1.27.jar 加载驱动:  1. 在工程目录中创建lib文件夹,将下载好的JDBC放到该文件夹下,如下图所示:    2. 右键工程名,在java build path中的Libraries分页中选择...

    工具:eclipse

       MySQL5.6

       MySQL连接驱动:mysql-connector-java-5.1.27.jar

    加载驱动:

      1. 在工程目录中创建lib文件夹,将下载好的JDBC放到该文件夹下,如下图所示:

      

      2. 右键工程名,在java build path中的Libraries分页中选择Add JARs...,选择刚才添加的JDBC,如下图:

      

    数据包准备:

      在数据库sqltestdb中创建如下数据表emp:

    1

    2

    3

    4

    5

    6

    7

    CREATE TABLE emp(

       empno            INT(4)          PRIMARY KEY,

       ename            VARCHAR(10),

       job              VARCHAR(9),

       hiredate         DATE,

       sal              FLOAT(7,2)

    ) ;

      添加数据:

      

    连接数据库并读取数据:

      数据库名称:sqltestdb

      数据包名称:emp

      端口号:3306

      用户名:root

      密码:root

    复制代码

     1 package sqldemo;
     2 
     3 import java.sql.Connection;
     4 import java.sql.DriverManager;
     5 import java.sql.ResultSet;
     6 import java.sql.SQLException;
     7 import java.sql.Statement;
     8 
     9 public class main {
    10 
    11     public static void main(String[] args) {
    12         //声明Connection对象
    13         Connection con;
    14         //驱动程序名
    15         String driver = "com.mysql.jdbc.Driver";
    16         //URL指向要访问的数据库名mydata
    17         String url = "jdbc:mysql://localhost:3306/sqltestdb";
    18         //MySQL配置时的用户名
    19         String user = "root";
    20         //MySQL配置时的密码
    21         String password = "123456";
    22         //遍历查询结果集
    23         try {
    24             //加载驱动程序
    25             Class.forName(driver);
    26             //1.getConnection()方法,连接MySQL数据库!!
    27             con = DriverManager.getConnection(url,user,password);
    28             if(!con.isClosed())
    29                 System.out.println("Succeeded connecting to the Database!");
    30             //2.创建statement类对象,用来执行SQL语句!!
    31             Statement statement = con.createStatement();
    32             //要执行的SQL语句
    33             String sql = "select * from emp";
    34             //3.ResultSet类,用来存放获取的结果集!!
    35             ResultSet rs = statement.executeQuery(sql);
    36             System.out.println("-----------------");
    37             System.out.println("执行结果如下所示:");  
    38             System.out.println("-----------------");  
    39             System.out.println("姓名" + "\t" + "职称");  
    40             System.out.println("-----------------");  
    41              
    42             String job = null;
    43             String id = null;
    44             while(rs.next()){
    45                 //获取stuname这列数据
    46                 job = rs.getString("job");
    47                 //获取stuid这列数据
    48                 id = rs.getString("ename");
    49 
    50                 //输出结果
    51                 System.out.println(id + "\t" + job);
    52             }
    53             rs.close();
    54             con.close();
    55         } catch(ClassNotFoundException e) {   
    56             //数据库驱动类异常处理
    57             System.out.println("Sorry,can`t find the Driver!");   
    58             e.printStackTrace();   
    59             } catch(SQLException e) {
    60             //数据库连接失败异常处理
    61             e.printStackTrace();  
    62             }catch (Exception e) {
    63             // TODO: handle exception
    64             e.printStackTrace();
    65         }finally{
    66             System.out.println("数据库数据成功获取!!");
    67         }
    68     }
    69 
    70 }

    复制代码

      运行结果:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    Succeeded connecting to the Database!

    -----------------

    执行结果如下所示:

    -----------------

    姓名  职称

    -----------------

    李兴华 经理

    张三  总监

    王五  厂长

    齐秦  书记

    张刚  组长

    曹操  财务

    李四  总裁

    数据库数据成功获取!!

    增加、删除和修改数据:

      增加数据:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    String name;

    String id;

      

    PreparedStatement psql;

    ResultSet res;

    //预处理添加数据,其中有两个参数--“?”

    psql = con.prepareStatement("insert into emp (empno,ename,job,hiredate,sal) "

            "values(?,?,?,?,?)");

    psql.setInt(13212);              //设置参数1,创建id为3212的数据

    psql.setString(2"王刚");      //设置参数2,name 为王刚

    psql.setString(3"总裁");

     

    DateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd");

    Date myDate2 = dateFormat2.parse("2010-09-13");

    psql.setDate(4,new java.sql.Date(myDate2.getTime()));

    psql.setFloat(5, (float2000.3);

    psql.executeUpdate();           //执行更新

      运行结果:

      

      更新数据:

    1

    2

    3

    4

    5

    6

     PreparedStatement psql;

    //预处理更新(修改)数据,将王刚的sal改为5000.0

    psql = con.prepareStatement("update emp set sal = ? where ename = ?");

    psql.setFloat(1,(float5000.0);      

    psql.setString(2,"王刚");             

    psql.executeUpdate();

      更改结果:

      

      删除数据:

    1

    2

    3

    4

    5

    6

    PreparedStatement psql;

    //预处理删除数据

    psql = con.prepareStatement("delete from emp where sal > ?");

    psql.setFloat(14500);

    psql.executeUpdate();

    psql.close();

      删除结果:

       

    展开全文
  • Java数据库的那些事(全是干货)

    千次阅读 2018-06-18 11:12:46
    谈到数据库,大家第一想法就是怎么去优化,怎么让查询操作更快。我认为最好的方式就是从开始数据库设计的时候就要尽量考虑周全。如果不幸是个老项目,就得从优化入手了。接下就从设计和优化谈一下我的一些认识和经验...

    谈到数据库,大家第一想法就是怎么去优化,怎么让查询操作更快。我认为最好的方式就是从开始数据库设计的时候就要尽量考虑周全。如果不幸是个老项目,就得从优化入手了。接下就从设计和优化谈一下我的一些认识和经验。

    1240

    1:数据库的设计

    一个好的数据库设计方案对于数据库的性能常常会起到事半功倍的效果。数据库的设计包含数据库架构和业务表的设计。         我刚整理了一套2018最新的0基础入门和进阶教程,无私分享,加Java学习裙 :六七八,二四一,五六三  即可获取,内附:开发工具和安装包,以及系统学习路线图

    1)数据库架构

    根据不同的数据量和访问量,来设计不同的架构。适合自己的才是最好的。

    单实例:数据读取和写入都是一个数据库实例。(备份实例不算在内)。这个适用于小型的企业内部系统。缺点是只适合数据量少的场景,优点是能达到数据的强一致性。

    1240

    垂直拆分,多实例。不同的业务走不同的实例。同样也是适用于单个业务,数据量不大,并且每个业务相对独立,不产生关联。

    1240

    读写分离,主从架构。通过主从结构,主库抗写压力,通过从库来分担读压力。适用于写少读多,数据一致性的实时性要求不高的应用。

    1240

    主从,集群结构。适用于写多,读也多的应用。实现是最为复杂的一种,需要考虑数据如何路由,后期扩容也相对麻烦。需要设计初期在理解业务的基础上,选择好合适的路由策略。例如range,hash等

    1240

    云数据库:阿里云rds等。适合没有专业数据库运维团队的公司,使用非常方便和简单。

    2)业务表的设计

    常见业务表分类:

    (1)配置表

    这种表通常存放业务一些基础的配置信息或者字典信息。表的数据量一般都比较小,修改变化的操作不太频繁,通常都是Select查询操作。

    (2)状态表

    这种表通常存放在业务系统中实体读象的状态信息,常见的有用户信息表,订单信息表等。这种表的数据量与实体读象的规模有直接关系,比如一个APP有多少注册用户,通常这个APP的用户表都会有多少条记录。状态表的变化通常比较频繁,而且Insert、Update、Select操作都会有,Delete操作是否有,通常会根据业务情况的规定决定。

    (3)日志表

    这种表通常用来记录业务系统中某种实体的状态信息,常见的有用户登录表、充值信息记录表等。这种表的数据规模通常比较大,而且如果业务状态变化频繁,记录的变化信息比较多,这种表的数据量和插入性能都要求比较高。日志表的操作,通常会以Insert操作为主,个别业务会对日志表进行查询。MySQL五种特殊需求架构中的高性能写入架构,主要就是应用这种表的需求。

    (4)归档表

    这种表,是将上面三种OLTP业务表的数据进行归档或者冷热分离的表。对线上业务三类表进行数据归档、冷热分离,一方面可以控制线上业务表的数据规模,保证业务表性能;另一方面进行归档后,可用于对归档历史数据进行更好的查询反映和支持。归档表的数据量大小与对应的线上表大小、归档周期有关。归档表的操作,除了归档过程的数据加载外,主要就是Select查询操作了,归档后就算是只读表。

    (5)统计数据表

    统计数据表,是指业务有离线统计分析需求时,需要将各种线上表和归档表的数据,通过ETL过程流转到线上OLAP统计分析系统中的原始数据表。这类表通常数据量会非常大,一个OLAP统计分析平台会汇总多个线上业务系统的数据进行统计分析。统计数据表的操作,除了数据流转动作外,主要就是各种统计分析程序的访问计算。

    (6)统计结果表

    统计结果表是在业务有离线统计分析需求时,各种统计分析过程访问统计数据表中的数据,按照一定的逻辑进行统计分析后的结果数据。这种统计结果数据,通常数据量会比较小。统计结果表的操作,处理结果流转动作外,主要就是供访问接口进行Select查询。

    对业务表类型的梳理,可以对所有的业务系统进行一个大体的划分,做到心中有数。

    下面是设计表的一些经验:

    将字段很多的表分解成多个表:对于字段较多的表,如果有些字段的使用频率很低,可以将这些字段分离出来,因为当一个表的数据量很大时,会由于使用频率低的字段的存在而变慢。

    增加中间表:对于需要经常联合查询的表,可以建立中间表以提高查询效率。

    增加冗余字段:合理的加入冗余字段可以提高查询速度。(冗余字段会导致一些问题,比如,冗余字段的值在一个表中被修改了,就要同步关联的表,否则会导致数据不一致。这要根据实际情况,平衡数据库性能,进行冗余字段的设计。)

    所有字段均定义为NOT NULL,除非你真的想存储null。

    提前做好数据量的预估,进行分表设计。不要等需要拆分时再拆,一般把表的数据量控制在千万级别。当单表数据量达到一定程度时(MySQL5.x时代的性能拐点则为1KW - 2KW行级别,具体需根据实际情况测试),为了提升性能,最为常用的方法就是分表。分表的策略可以是垂直拆分(比如:不同订单状态的订单拆分到不同的表),也可以是水平拆分(比如:按月将订单拆分到不同表)。如果在业务层分表,会将逻辑变得复杂,而且分散。可以引入分表的中间件屏蔽分表后的细节,让业务层像查询单表一样查询分表后的数据。比如Mycat。(访问量不大,但是表数据很多的表,我们可以采取分区表,实现起来也比较简单)

    选择统一的字符集。MySQL采用“阶梯”式的方式来设定字符集默认值,每个数据库,每张表都有自己的默认值,它们逐层继承,最终最靠底层的默认设置将影响你创建的对象。不同字符集和校对规则之间的转换可能会带来额外的系统开销,影响数据库的性能。

    合理的设置主键和索引。

    主键分自增主键和业务主键。

    自增主键:写入、查询效率和磁盘利用率都高,但每次查询都需要两级索引,因为线上业务不会有直接使用主键列的查询。

    业务主键:写入、查询效率和磁盘利用率都低,但可以使用一级索引,依赖覆盖索引的特性,某些情况下在非主键索引上也可以实现1次索引完成查询

    一般情况下都是采用业务主键。

    索引大概分为三类。

    主键索引:InnoDB会自动在表的主键上创建索引,数据结构使用B+Tree。

    非主键索引:非主键列上的索引为二级索引(因为一次查询需要查找两个索引树)

    联合索引:联合索引也叫多列索引,索引结构的key包含多个字段,排序时先第一列比较,如果相同再按第二列比较,以此类推。

    2:数据库的优化(mysql)

    说起mysql优化,一定要了解一下mysql原理,这样才能深入的理解那些sql规则。下图展示了MySQL的逻辑架构图。

    1240

    MySQL客户端/服务端通信协议是“半双工”的:在任一时刻,要么是服务器向客户端发送数据,要么是客户端向服务器发送数据,这两个动作不能同时发生。一旦一端开始发送消息,另一端要接收完整个消息才能响应它,所以我们无法也无须将一个消息切成小块独立发送,也没有办法进行流量控制。客户端用一个单独的数据包将查询请求发送给服务器,所以当查询语句很长的时候,需要设置max_allowed_packet参数。当服务器响应客户端请求时,客户端必须完整的接收整个返回结果,而不能简单的只取前面几条结果,然后让服务器停止发送。因而在实际开发中,尽量保持查询简单且只返回必需的数据,减小通信间数据包的大小和数量是一个非常好的习惯,这也是查询中尽量避免使用SELECT *以及加上LIMIT限制的原因之一。

    1)sql的优化。通过explain和show profiles进行执行计划分析,找出问题,进行针对性的优化。其中创建高效索引是最有效的一个手段。

    多列索引和索引顺序:

    出现多个索引做相交操作时(多个AND条件),通常来说一个包含所有相关列的索引要优于多个独立索引。

    在选择性高的字段上建立索引,可以让MySQL在查询时过滤掉更多的行。对于多列索引,哪个索引字段在前面,取决于索引的选择性的高低。选择性高的索引排在前面,有利于提高查询效率。例如联合索引(user_group_id,trade_amount)用户的群组肯定比订单的交易金额的选择性高。

    覆盖索引:如果一个索引包含或者说覆盖所有需要查询的字段的值,那么就没有必要再回表查询,这就称为覆盖索引。覆盖索引是非常有用的工具,可以极大的提高性能,因为查询只需要扫描索引会带来许多好处:

    优化关联查询:以小表驱动大表。

    子查询尽量换成join。这是因为join,MySQL不需要在内存中创建临时表来完成这个逻辑上的需求。

    确保任何的GROUP BY和ORDER BY中的表达式只涉及到一个表中的列,这样MySQL才有可能使用索引来优化。

    优化LIMIT分页:一个常见的问题是当偏移量非常大的时候,比如:LIMIT 10000 20这样的查询,MySQL需要查询10020条记录然后只返回20条记录,前面的10000条都将被抛弃,这样的代价非常高。优化这种查询一个最简单的办法就是尽可能的使用覆盖索引扫描,而不是查询所有的列。然后根据需要做一次关联查询再返回所有的列。对于偏移量很大时,这样做的效率会提升非常大。考虑下面的查询:

    修改前:SELECT film_id,description FROM film ORDER BY title LIMIT 50,5;

    修改后:

    SELECT film.film_id,film.description

    FROM film INNER JOIN (

    SELECT film_id FROM film ORDER BY title LIMIT 50,5

    ) AS tmp USING(film_id);

    优化UNION:除非确实需要服务器去重,否则就一定要使用UNION ALL,如果没有ALL关键字,MySQL会给临时表加上DISTINCT选项,这会导致整个临时表的数据做唯一性检查,这样做的代价非常高。

    避免导致索引失效的写法

    (1)负向条件查询不能使用索引(not in/not exists都不是好习惯)

    (2)前导模糊查询不能使用索引(like'XX%')

    (3)数据区分度不大的字段不宜使用索引

    (4)在属性上进行计算不能命中索引

    (5)复合索引最左前缀不满足

    强制类型转换会全表扫描

    如果明确知道只有一条结果返回,limit 1能够提高效率

    2)合理设置mysql的部分参数,达到最高效。

    thread_pool_size:如果主引擎(primary storage engine)为InnoDB,thread_pool_size最佳设置可能在16和36之间,最常见的优化值倾向于24到36。

    thread_pool_stall_limit:用处理被阻塞和长时间运行的语句,确保服务器不完全被阻塞。设置过长会导致线程被阻塞,引起性能问题。

    tmp_table_size:通过设置tmp_table_size选项来增加一张临时表的大小,例如做order by ,GROUP BY操作生成的临时表。如果调高该值,MySQL同时将增加heap表的大小,可达到提高联接查询速度的效果,建议尽量优化查询,要确保查询过程中生成的临时表在内存中,避免临时表过大导致生成基于硬盘的MyISAM表

    展开全文
  • Java 数据库基本操作

    千次阅读 2018-07-15 15:43:37
    连接数据库要访问数据库,首先要加载数据库的驱动程序(只需要在第一次访问数据库时加载一次),然后每次访问数据时创建一个Connection对象,之后执行操作数据库的SQL语句,最后在完成数据库操作后销毁前面创建的...

    连接数据库

    要访问数据库,首先要加载数据库的驱动程序(只需要在第一次访问数据库时加载一次),然后每次访问数据时创建一个Connection对象,之后执行操作数据库的SQL语句,最后在完成数据库操作后销毁前面创建的Connection对象,释放与数据库的连接。


    import java.sql.*; //导入java.sql包
    
    public class Conn { // 创建类Conn
    	Connection con; // 声明Connection对象
    
    	public Connection getConnection() { // 建立返回值为Connection的方法
    		try { // 加载数据库驱动类
    			Class.forName("com.mysql.jdbc.Driver");
    			System.out.println("数据库驱动加载成功");
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		}
    		try { // 通过访问数据库的URL获取数据库连接对象
    			con = DriverManager.getConnection("jdbc:mysql:"
    					+ "//127.0.0.1:3306/test", "root", "123456");
    			System.out.println("数据库连接成功");
    		} catch (SQLException e) {
    			e.printStackTrace();
    		}
    		return con; // 按方法要求返回一个Connection对象
    	}
    
    	public static void main(String[] args) { // 主方法
    		Conn c = new Conn(); // 创建本类对象
    		c.getConnection(); // 调用连接数据库的方法
    	}
    }
    

    向数据库发送SQL语句

    getConnection()方法只是获取与数据库的连接,要执行SQL语句首先要获得Statement类对象,通过创建的连接数据库对象的createStatement()方法可获得Statement对象。

    处理查询结果集

    有了Statement对象以后,可调用相应的方法实现对数据库的查询和修改,并将查询的结果集存放在ResultSet类的对象中。

    顺序查询

    ResultSet类的next()方法的返回值是boolean类型的数据,当游标移动到最后一行之后会返回false。

    import java.sql.*;
    
    public class Gradation { // 创建类
    	static Connection con; // 声明Connection对象
    	static Statement sql; // 声明Statement对象
    	static ResultSet res; // 声明ResultSet对象
    
    	public Connection getConnection() { // 连接数据库方法
    
    		try {
    			Class.forName("com.mysql.jdbc.Driver");
    			con = DriverManager.getConnection("jdbc:mysql:"
    					+ "//127.0.0.1:3306/test", "root", "123456");
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return con; // 返回Connection对象
    	}
    
    	public static void main(String[] args) { // 主方法
    		Gradation c = new Gradation(); // 创建本类对象
    		con = c.getConnection(); // 与数据库建立连接
    		try {
    			sql = con.createStatement(); // 实例化Statement对象
    			// 执行SQL语句,返回结果集
    			res = sql.executeQuery("select * from tb_stu");
    			while (res.next()) { // 如果当前语句不是最后一条则进入循环
    				String id = res.getString("id"); // 获取列名是"id"的字段值
    				// 获取列名是"name"的字段值
    				String name = res.getString("name");
    				// 获取列名是"sex"的字段值
    				String sex = res.getString("sex");
    				// 获取列名是"birthday"的字段值
    				String birthday = res.getString("birthday");
    				System.out.print("编号:" + id); // 将列值输出
    				System.out.print(" 姓名:" + name);
    				System.out.print(" 性别:" + sex);
    				System.out.println(" 生日:" + birthday);
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }

    模糊查询

    SQL语句中提供了LIKE操作符用于模糊查询,可使用“%”来代替0个或多个字符,使用下划线“_”来代替一个字符。

    import java.sql.*;
    
    public class Train { // 创建类Train
    	static Connection con; // 声明Connection对象
    	static Statement sql; // 声明Statement对象
    	static ResultSet res; // 声明ResultSet对象
    	
    	public Connection getConnection() { // 与数据库连接方法
    		try {
    			Class.forName("com.mysql.jdbc.Driver");
    			con = DriverManager.getConnection("jdbc:mysql:"
    					+ "//127.0.0.1:3306/test", "root", "123456");
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return con; // 返回Connection对象
    	}
    	
    	public static void main(String[] args) { // 主方法
    		Train c = new Train(); // 创建本类对象
    		con = c.getConnection(); // 获取与数据库的连接
    		try { // try语句捕捉异常
    			sql = con.createStatement(); // 实例化Statement对象
    			res = sql.executeQuery("select * from tb_stu where name like '张%'");// 执行SQL语句
    			while (res.next()) { // 如果当前记录不是结果集中的最后一条,进入循环体
    				String id = res.getString(1); // 获取id字段值
    				String name = res.getString("name"); // 获取name字段值
    				String sex = res.getString("sex"); // 获取sex字段值
    				String birthday = res.getString("birthday"); // 获取birthday字段值
    				System.out.print("编号:" + id); // 输出信息
    				System.out.print(" 姓名:" + name);
    				System.out.print(" 性别:" + sex);
    				System.out.println(" 生日:" + birthday);
    			}
    		} catch (Exception e) { // 处理异常
    			e.printStackTrace(); // 输出异常信息
    		}
    	}
    }

    预处理语句

            向数据库发送一个SQL语句,数据库中的SQL解释器负责把SQL语句生成底层的内部命令,然后执行该命令,完成相关的数据操作。如果不断地向数据库提交SQL语句,肯定会增加数据库中SQL解释器的负担,影响执行的速度。

            对于JDBC,可以通过Connection对象的preparedStatement(String sql)方法对SQL语句进行预处理,生成数据库底层的内部命令,并将该命令封装在PreparedStatement对象中,通过调用该对象的相应方法执行底层数据库命令。这样应用程序能针对连接的数据库实现将SQL语句解释为数据库底层的内部命令,然后让数据库执行这个命令,这样可以减轻数据库的负担,提高访问数据库的速度。

    import java.sql.*;
    public class Prep { // 创建类Perp
    	static Connection con; // 声明Connection对象
    	static PreparedStatement sql; // 声明预处理对象
    	static ResultSet res; // 声明结果集对象
    	public Connection getConnection() { // 与数据库连接方法
    		try {
    			Class.forName("com.mysql.jdbc.Driver");
    			con = DriverManager.getConnection("jdbc:mysql:"
    					+ "//127.0.0.1:3306/test", "root", "123456");
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return con; // 返回Connection对象
    	}
    	
    	public static void main(String[] args) { // 主方法
    		Prep c = new Prep(); // 创建本类对象
    		con = c.getConnection(); // 获取与数据库的连接
    		try {
    			// 实例化预处理对象
    			sql = con.prepareStatement("select * from tb_stu"
    					+ " where id = ?");
    			sql.setInt(1, 4); // 设置参数
    			res = sql.executeQuery(); // 执行预处理语句
    			// 如果当前记录不是结果集中最后一行,则进入循环体
    			while (res.next()) {
    				String id = res.getString(1); // 获取结果集中第一列的值
    				String name = res.getString("name"); // 获取name列的列值
    				String sex = res.getString("sex"); // 获取sex列的列值
    				// 获取birthday列的列值
    				String birthday = res.getString("birthday");
    				System.out.print("编号:" + id); // 输出信息
    				System.out.print(" 姓名:" + name);
    				System.out.print(" 性别:" + sex);
    				System.out.println(" 生日:" + birthday);
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }

    添加、修改、删除记录

    可以通过SQL语句对数据执行添加、修改和删除操作。可通过PreparedStatement类的指定参数动态地对数据表中原有数据进行修改操作,并通过executeUpdate()方法执行更新语句。

    import java.sql.*;
    
    public class Renewal { // 创建类
    	static Connection con; // 声明Connection对象
    	static PreparedStatement sql; // 声明PreparedStatement对象
    	static ResultSet res; // 声明ResultSet对象
    
    	public Connection getConnection() {
    		try {
    			Class.forName("com.mysql.jdbc.Driver");
    			con = DriverManager.getConnection("jdbc:mysql:"
    					+ "//127.0.0.1:3306/test", "root", "123456");
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return con;
    	}
    
    	public static void main(String[] args) {
    		Renewal c = new Renewal(); // 创建本类对象
    		con = c.getConnection(); // 调用连接数据库方法
    		try {
    			sql = con.prepareStatement("select * from tb_stu"); // 查询数据库
    			res = sql.executeQuery(); // 执行SQL语句
    			System.out.println("执行增加、修改、删除前数据:");
    			while (res.next()) {
    				String id = res.getString(1);
    				String name = res.getString("name");
    				String sex = res.getString("sex");
    				String birthday = res.getString("birthday"); // 遍历查询结果集
    				System.out.print("编号:" + id);
    				System.out.print(" 姓名:" + name);
    				System.out.print(" 性别:" + sex);
    				System.out.println(" 生日:" + birthday);
    			}
    			sql = con.prepareStatement("insert into tb_stu(name,sex,birthday) values(?,?,?)");
    			sql.setString(1, "张一"); // 预处理添加数据
    			sql.setString(2, "女");
    			sql.setString(3, "2012-12-1");
    			sql.executeUpdate();
    			sql = con.prepareStatement("update tb_stu set birthday "
    					+ "= ? where id = ? ");
    			sql.setString(1, "2012-12-02"); // 更新数据
    			sql.setInt(2, 1); // 更新数据
    			sql.executeUpdate();
    			Statement stmt = con.createStatement();
    			stmt.executeUpdate("delete from tb_stu where id = 1");
    			// 查询修改数据后的tb_stu表中数据
    			sql = con.prepareStatement("select * from tb_stu");
    			res = sql.executeQuery(); // 执行SQL语句
    			System.out.println("执行增加、修改、删除后的数据:");
    			while (res.next()) {
    				String id = res.getString(1);
    				String name = res.getString("name");
    				String sex = res.getString("sex");
    				String birthday = res.getString("birthday");
    				System.out.print("编号:" + id);
    				System.out.print(" 姓名:" + name);
    				System.out.print(" 性别:" + sex);
    				System.out.println(" 生日:" + birthday);
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    展开全文
  • Java基础-数据库-简单总结

    千次阅读 2018-05-20 19:33:58
    总结数据库,非常简单的那种总结,简单到不想拿来复习的那种。数据库(DataBase,简称DB) 概念:就是存放数据的仓库,是为了实现一定目的,按照某种规则组织起来的数据的集合。 必要性:1.可以结构化存储大量的...
  • Java】如何用Java实现数据库查询

    万次阅读 2017-11-17 17:39:06
    首先要创建需要查询的文件在你的文件里。...import java.io.*; import java.util.*; public class DAO { //通过学生姓名找到学生学号 private String findSnumBySname(String Sname) { String result
  • java+数据库

    2020-07-30 23:31:50
    数据库+java的软件,包括增删改查等操作,
  • Java进行数据库的连接

    万次阅读 多人点赞 2017-10-20 12:48:55
    因为项目进展需要用到数据库,以前在使用C\C++进行数据库的操作的时候感觉还比较简单,但是在转到JAVA平台上的时候,发现步骤还比较繁琐,加之各种书本对JAVA安装数据库的步骤都写的模棱两可。 本教程针对已经安装好...
  • Java连接数据库的几种方式

    万次阅读 2017-10-26 16:50:51
    第一种:最普通的方式,利用java原生jdbc连接数据库 package utils;   import java.sql.Connection; importjava.sql.DriverManager; importjava.sql.PreparedStatement; import java.sql.ResultSet;
  • 现在主流的Java开发人员一般都需要会主流数据库。开发人员需要对Mysql、Oracle、SqlServer这三个常用的熟悉了解。在数据量比较大或者请求数比较高的情况下,需要了解一些特定数据库针对性优化。SQL相关入门熟悉表、...
  • Java数据库面试题

    千次阅读 2017-10-29 16:04:29
    jdbc:叫做Java数据库连接 jdbc接口及相关类在Java.sql包和javax.sql包中, jdbc接口让Java程序和jdbc驱动实现了松耦合,是的切换不同的额数据库变得更加简单。jdbc时如何实现Java程序和jdbc驱动的松耦合? jdbc...
  • java数据库系统开发案例精选

    千次阅读 2008-01-24 09:14:00
    中文名称:java数据库系统开发案例精选资源类型:ISO版本:随书光盘发行时间:2007年03月地区:大陆语言:普通话简介:明日科技 王国辉 吕海涛 李钟尉 编著 人民邮电出版社《java数据库系统开发案例精选》 随书光盘...
  •  数据库访问几乎每一个稍微成型的程序都要用到的知识,怎么高效的访问数据库也是我们学习的一个重点,今天的任务就是总结java访问数据库的方法和有关API,java访问数据库主要用的方法是JDBC,它是java语言中用来规范...
  • Java数据库连接池几种方式及其特点

    千次阅读 2018-05-07 10:37:23
    主流的数据库连接池 ...DBCP:由Apache开发的一个Java数据库连接池项目, Jakarta commons-pool对象池机制,Tomcat使用的连接池组件就是DBCP。单独使用dbcp需要3个包:common-dbcp.jar,common-pool.jar,com...
  • 1.JDBC(Java Database Connectivity:Java数据库连接)内容概览1.JDBC全称:Java DataBase Connectivity,SUN公司提供的一套操作数据库的标准规范;2.JDBC代表Java数据库链接,与数据库驱动的关系就如接口与实现的...
  • Java数据库连接class.forName()的原因

    千次阅读 2016-11-20 14:19:48
    Java数据库连接class.forName()的原因说明Java中的数据库驱动程序: 数据库的驱动程序是指实现java数据库连接的相关方法。Java提供数据库连接的相关标准(各种接口类),然后比如mysql,orical来实现Java连接数据库的...
  • Java数据库连接池实现原理

    万次阅读 多人点赞 2016-05-29 16:39:12
    一般来说,Java应用程序访问数据库的过程是:  ①装载数据库驱动程序;  ②通过jdbc建立数据库连接;  ③访问数据库,执行sql语句;  ④断开数据库连接。 public class DBConnection { private Connection ...
  • 学习JDK内置纯JAVA数据库Derby

    千次阅读 2016-07-03 12:12:34
    JDK内置纯JAVA数据库Derby从学习JAVA到现在好多年了,不过中间有很长一段时间没怎么用JAVA和相关技术了,都不清楚原来JDK本身内置了Derby数据库。Derby数据库特点1.纯JAVA编写2.极其小巧,核心只有2M左右3.JDK内置...
  • Java数据库数据比较问题

    千次阅读 2017-06-23 20:47:59
    Java连接数据库,并比较数据库中两张表的数据是否相同, 相同打印true, 不相同打印false。 请大神们教小白, 万分感谢!!!!!!!
  • java数据库调用

    千次阅读 2019-08-27 15:13:54
    1.概念:Java Database Connectivity java数据库连接 ​ 本质:其实是官方(SUN公司)提供的一套操作所有关系型数据库的规则(接口),各个数据库厂商会去实现这套接口,产生数据库驱动(Jar包),我们可以使用这套接口...
1 2 3 4 5 ... 20
收藏数 1,383,193
精华内容 553,277
关键字:

java 数据库