数据库连接之JDBC编程

JDBC

  • 名词解释:java database Connector java与数据库的连接桥梁(一套接口)

开发步骤

  1. 加载驱动
Class.forName("驱动类名(com.mysql.jdbc.Driver)");
  1. 创建数据库连接,返回一个connection对象
Connection conn =DriverManager.getConnection(连接字符串, 用户名, 密码);

连接字符串:jdbc:mysql://ip地址:端口号(3306)/数据库名?各种参数;

这里补充下端口号的修改(默认33061、打开cmd;
2、输入命令:net stop +MySQL的服务名,停止MySQL服务,如果未启动MySQL服务则可跳过该步骤;
3、输入命令:mysqld --remove卸载MySQL服务,如果未安装MySQL服务则可跳过该步骤;
4、在MySQL的安装目录下用记事本打开my-default.ini文件,取消[mysqld]中的port注释,并将port设置成您想要更改的端口号,保存退出;
4、输入命令:mysqld --install MySQL的服务名--defaults-file=“my-default.ini的路径”,安装MySQL服务;
5、提示安装成功后,输入命令:net start MySQL的服务名,启动MySQL服务。
  1. 创建Statment(Preparedstatement)对象
Statement stmt = conn.createStatement();
执行增删改
int rows = executeUpdate(sql语句); //返回影响书库的行数

执行查询
ResultSet rs = executeQuery(sql语句); //返回查询的结果集

4.关闭连接

conn.close();

注入攻击问题

  • 上述执行mysql语句的时候,mysql使用的是字符串拼接的手段,然而就暴露了一个严重的问题,可以利用字符串漏洞实现。
下面是一个登陆查询时候的mysql语句
"select * from xx_user where username = '"+username+"' and password = '"+password+"'";

然而将 (username="laowang", paseword="aaa' or '1'='1");带入mysql语句。(数据库里并没有存储这样的数据)

结果:
"select * from xx_user where username = 'laowang' and password = 'aaa' or '1'='1'";

会发现:在密码部分 password始终是一个逻辑真值。
就算我们没有输入正确的密码,这天mysql语句照样返回true。误提示我们查询到。

解决方法:PrepareStatement对象

解决办法:

  1. 对参数内存做检查,内部不能有sql关键字例如:or
  2. PreparedStatement
  • 在jdbc第三步时候
PrepareStatement preparestatement=connection.prepareStatement(mysql语句)
  • 在PrepareStatement的sql语句里,可以使用占位符(?)将所需要的数据空出,之后使用preparestatement.setXxx(占位符位置(起始为1),“值”)方法。
例如:
PreparedStatement  psmt = connection.prepareStatement("insert into student(sid,sname,birthday,sex) values (null ,?,?,?)"
psmt.setString(1,name);
psmt.setString(2,brthday);
psmt.setString(3,sex);
int i = psmt.executeUpdate();   //execute return boolean
System.out.println("执行成功,添加了"+i+"行");
  • ?只能代表值,不能是关键字,表名,列名
  • 调用的方法根据?的实际类型而定,例如值是int 调用setInt方法
  • ?的对应的set方法下标从1开始

JDBC性能问题

正常的JDBCmysql语句执行流程

  1. 将sql语句从客户端程序发送给数据库服务器
  2. 由命令解析器进行词法分析、语法分析、生成解析树
  3. 如果是查询,还要生成查询计划,对sql执行进行优化
  4. 由访问控制模块检查权限、生成新的解析树
  5. 进入表管理模块,打开对应的表文件
  6. 调用存储引擎,执行
  7. 将结果返回给客户端程序

重用问题(解决:预编译)

  • jdbc中要利用预编译的功能,需要使用PreparedStatement,普通Statement不行,另外对于MySQL来说,要在jdbc 连接字符串中加入参数:
- useServerPrepStmts=true&cachePrepStmts=true

其中:

  • useServerPrepStmts=true是开启MySQL的预编译功能,即PreparedStatement对象会利用prepare语句
  • cachePrepStmts=true是同一个连接的多个PreparedStatement对象能够被缓存,否则一旦PreparedStatement对象关闭,则下一个PreparedStatement对象执行相同sql时,还是会重新执行prepare

批处理(解决:addBatch和executeBatch)

  • 在增加数据时,有事会需要增加很多数据,单次增加太耗时间,所以引入了批处理
  • 需要的参数
rewriteBatchedStatements=true
  • 例如:
执行完mysql语句后,直接返回结果
1.preparedStatement.executeUpdate();   //单次更新

利用addbatch方法,将一组参数添加到此 PreparedStatement 对象的批处理命令中。
1.preparedStatement.addBatch();      //批量更新
2.preparedStatement.executeLargeBatch();   //批量更新

查询性能

  • 提升查询性能最有效的方法,建立数据库索引
  • 索引是对现有的数据进行排序,在排序的结果中搜索,效率会很高
  • 索引的数据结构是:B+树
  • mysql的主键会自动创建索引,用主键作为查询条件,搜索速度最快
创建
create index 索引名 on 表();
// 向 big 的name 建立一个索引
create index idx_name on big(name);

删除索引
alter table 表名 drop key 索引名
alter table big drop key idx_name;

索引使用的注意事项:

  • 使用了空间换了时间,会占用额外存储
  • 索引提升查询性能的同时,影响数据的增删改( 在这张表的查询远高于增删改时,建立索引有意义 )
  • null 值不适合索引,要加索引的列上一般添加一个not null约束, 并给一个默认值
  • 区分度低的列不适合建索引
  • mysql 主键索引(聚簇索引, 把这一行的值都存储到叶子节点上) 和 普通索引(叶子节点只存储了主键的值和索引列的值)
    所以查询普通索引时,会搜索两遍,首先走普通索引,再走主键索引,(称为回表)
  • 从大量的数据中找到少量数据时,才能充分利用索引

避免回表的办法

  1. 不用滥用select *
  2. 建立复合(组合)索引

连接池

  • apache dbcp 最老牌
  • c3p0 连接池
  • alibaba druid (德鲁伊) sql监控
druid (德鲁伊)
static final DruidDataSource dataSource = new DruidDataSource();
    static {
        dataSource.setUrl(url_jwgl);
        dataSource.setUsername(usename);
        dataSource.setPassword(password);
//        dataSource.setDriverClassName("com.mysql.jdbc.Driver"); 可选步骤,注册驱动
        dataSource.setInitialSize(5); // 初始连接数
        dataSource.setMaxActive(10); // 最大连接数
        dataSource.setMinIdle(5);    // 最小连接数
        dataSource.setValidationQuery("select 1"); // 一条简单的sql语句,用来保活
        dataSource.setTestWhileIdle(true); // 当空闲时时不时检查一下连接的有效性, 利用ValidationQuery中的sql语句
        dataSource.setTimeBetweenEvictionRunsMillis(60*1000); // 默认一分钟
    }
dataSource.getConnection();
  • 相似的连接池工具基本用法一样,这里不再举例说明
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页