跳到主要内容

其他数据库相关

JDBC

概述

JDBC(Java DataBase Connectivity,Java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系型数据库提供统一访问,是由一组用 Java 语言编写的类和接口组成的。

JDBC 是 Java 官方提供的一套规范(接口),用于帮助开发人员快速实现不同关系型数据库的连接


功能类

DriverManager

DriverManager:驱动管理对象

  • 注册驱动:

    • 注册给定的驱动:public static void registerDriver(Driver driver)

    • 代码实现语法:Class.forName("com.mysql.jdbc.Driver)

    • com.mysql.jdbc.Driver 中存在静态代码块

      static {
      try {
      DriverManager.registerDriver(new Driver());
      } catch (SQLException var1) {
      throw new RuntimeException("Can't register driver!");
      }
      }
    • 不需要通过 DriverManager 调用静态方法 registerDriver,因为 Driver 类被使用,则自动执行静态代码块完成注册驱动

    • jar 包中 META-INF 目录下存在一个 java.sql.Driver 配置文件,文件中指定了 com.mysql.jdbc.Driver

  • 获取数据库连接并返回连接对象:

    方法:public static Connection getConnection(String url, String user, String password)

    • url:指定连接的路径,语法为 jdbc:mysql://ip地址(域名):端口号/数据库名称
    • user:用户名
    • password:密码

Connection

Connection:数据库连接对象

  • 获取执行者对象
    • 获取普通执行者对象:Statement createStatement()
    • 获取预编译执行者对象:PreparedStatement prepareStatement(String sql)
  • 管理事务
    • 开启事务:setAutoCommit(boolean autoCommit),false 开启事务,true 自动提交模式(默认)
    • 提交事务:void commit()
    • 回滚事务:void rollback()
  • 释放资源
    • 释放此 Connection 对象的数据库和 JDBC 资源:void close()

Statement

Statement:执行 sql 语句的对象

  • 执行 DML 语句:int executeUpdate(String sql)
    • 返回值 int:返回影响的行数
    • 参数 sql:可以执行 insert、update、delete 语句
  • 执行 DQL 语句:ResultSet executeQuery(String sql)
    • 返回值 ResultSet:封装查询的结果
    • 参数 sql:可以执行 select 语句
  • 释放资源
    • 释放此 Statement 对象的数据库和 JDBC 资源:void close()

ResultSet

ResultSet:结果集对象,ResultSet 对象维护了一个游标,指向当前的数据行,初始在第一行

  • 判断结果集中是否有数据:boolean next()
    • 有数据返回 true,并将索引向下移动一行
    • 没有数据返回 false
  • 获取结果集中当前行的数据:XXX getXxx("列名")
    • XXX 代表数据类型(要获取某列数据,这一列的数据类型)
    • 例如:String getString("name"); int getInt("age");
  • 释放资源
    • 释放 ResultSet 对象的数据库和 JDBC 资源:void close()

代码实现

数据准备

-- 创建db14数据库
CREATE DATABASE db14;

-- 使用db14数据库
USE db14;

-- 创建student表
CREATE TABLE student(
sid INT PRIMARY KEY AUTO_INCREMENT, -- 学生id
NAME VARCHAR(20), -- 学生姓名
age INT, -- 学生年龄
birthday DATE, -- 学生生日
);

-- 添加数据
INSERT INTO student VALUES (NULL,'张三',23,'1999-09-23'),(NULL,'李四',24,'1998-08-10'),
(NULL,'王五',25,'1996-06-06'),(NULL,'赵六',26,'1994-10-20');

JDBC 连接代码:

public class JDBCDemo01 {
public static void main(String[] args) throws Exception{
//1.导入jar包
//2.注册驱动
Class.forName("com.mysql.jdbc.Driver");

//3.获取连接
Connection con = DriverManager.getConnection("jdbc:mysql://192.168.2.184:3306/db2","root","123456");

//4.获取执行者对象
Statement stat = con.createStatement();

//5.执行sql语句,并且接收结果
String sql = "SELECT * FROM user";
ResultSet rs = stat.executeQuery(sql);

//6.处理结果
while(rs.next()) {
System.out.println(rs.getInt("id") + "\t" + rs.getString("name"));
}

//7.释放资源
con.close();
stat.close();
con.close();
}
}


注入攻击

攻击演示

SQL 注入攻击演示

  • 在登录界面,输入一个错误的用户名或密码,也可以登录成功

    SQL注入攻击演示

  • 原理:我们在密码处输入的所有内容,都应该认为是密码的组成,但是 Statement 对象在执行 SQL 语句时,将一部分内容当做查询条件来执行

    SELECT * FROM user WHERE loginname='aaa' AND password='aaa' OR '1'='1';

攻击解决

PreparedStatement:预编译 sql 语句的执行者对象,继承 PreparedStatement extends Statement

  • 在执行 sql 语句之前,将 sql 语句进行提前编译,明确 sql 语句的格式,剩余的内容都会认为是参数
  • sql 语句中的参数使用 ? 作为占位符

为 ? 占位符赋值的方法:setXxx(int parameterIndex, xxx data)

  • 参数1:? 的位置编号(编号从 1 开始)

  • 参数2:? 的实际参数

    String sql = "SELECT * FROM user WHERE loginname=? AND password=?";
    pst = con.prepareStatement(sql);
    pst.setString(1,loginName);
    pst.setString(2,password);

执行 sql 语句的方法

  • 执行 insert、update、delete 语句:int executeUpdate()
  • 执行 select 语句:ResultSet executeQuery()

连接池

概念

数据库连接背景:数据库连接是一种关键的、有限的、昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。

数据库连接池:数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个,这项技术能明显提高对数据库操作的性能。

数据库连接池原理

数据库连接池原理图解


归还连接

使用动态代理的方式来改进

自定义数据库连接池类:

public class MyDataSource implements DataSource {
//1.准备一个容器。用于保存多个数据库连接对象
private static List<Connection> pool = Collections.synchronizedList(new ArrayList<>());

//2.定义静态代码块,获取多个连接对象保存到容器中
static{
for(int i = 1; i <= 10; i++) {
Connection con = JDBCUtils.getConnection();
pool.add(con);
}
}
//3.提供一个获取连接池大小的方法
public int getSize() {
return pool.size();
}

//动态代理方式
@Override
public Connection getConnection() throws SQLException {
if(pool.size() > 0) {
Connection con = pool.remove(0);

Connection proxyCon = (Connection) Proxy.newProxyInstance(
con.getClass().getClassLoader(), new Class[]{Connection.class},
new InvocationHandler() {
/*
执行Connection实现类连接对象所有的方法都会经过invoke
如果是close方法,归还连接
如果不是,直接执行连接对象原有的功能即可
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("close")) {
//归还连接
pool.add(con);
return null;
}else {
return method.invoke(con,args);
}
}
});
return proxyCon;
}else {
throw new RuntimeException("连接数量已用尽");
}
}
}

开源项目

C3P0

使用 C3P0 连接池:

  • 配置文件名称:c3p0-config.xml,必须放在 src 目录下

    <c3p0-config>
    <!-- 使用默认的配置读取连接池对象 -->
    <default-config>
    <!-- 连接参数 -->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://192.168.2.184:3306/db14</property>
    <property name="user">root</property>
    <property name="password">123456</property>

    <!-- 连接池参数 -->
    <!--初始化数量-->
    <property name="initialPoolSize">5</property>
    <!--最大连接数量-->
    <property name="maxPoolSize">10</property>
    <!--超时时间 3000ms-->
    <property name="checkoutTimeout">3000</property>
    </default-config>

    <named-config name="otherc3p0">
    <!-- 连接参数 -->
    <!-- 连接池参数 -->
    </named-config>
    </c3p0-config>
  • 代码演示

    public class C3P0Test1 {
    public static void main(String[] args) throws Exception{
    //1.创建c3p0的数据库连接池对象
    DataSource dataSource = new ComboPooledDataSource();

    //2.通过连接池对象获取数据库连接
    Connection con = dataSource.getConnection();

    //3.执行操作
    String sql = "SELECT * FROM student";
    PreparedStatement pst = con.prepareStatement(sql);

    //4.执行sql语句,接收结果集
    ResultSet rs = pst.executeQuery();

    //5.处理结果集
    while(rs.next()) {
    System.out.println(rs.getInt("sid") + "\t" + rs.getString("name") + "\t" + rs.getInt("age") + "\t" + rs.getDate("birthday"));
    }

    //6.释放资源
    rs.close(); pst.close(); con.close();
    }
    }

Druid

Druid 连接池:

  • 配置文件:druid.properties,必须放在 src 目录下

    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://192.168.2.184:3306/db14
    username=root
    password=123456
    initialSize=5
    maxActive=10
    maxWait=3000
  • 代码演示

    public class DruidTest1 {
    public static void main(String[] args) throws Exception{
    //获取配置文件的流对象
    InputStream is = DruidTest1.class.getClassLoader().getResourceAsStream("druid.properties");

    //1.通过Properties集合,加载配置文件
    Properties prop = new Properties();
    prop.load(is);

    //2.通过Druid连接池工厂类获取数据库连接池对象
    DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);

    //3.通过连接池对象获取数据库连接进行使用
    Connection con = dataSource.getConnection();

    //4.执行sql语句,接收结果集
    String sql = "SELECT * FROM student";
    PreparedStatement pst = con.prepareStatement(sql);
    ResultSet rs = pst.executeQuery();

    //5.处理结果集
    while(rs.next()) {
    System.out.println(rs.getInt("sid") + "\t" + rs.getString("name") + "\t" + rs.getInt("age") + "\t" + rs.getDate("birthday"));
    }

    //6.释放资源
    rs.close(); pst.close(); con.close();
    }
    }


Jedis

基本使用

Jedis 用于 Java 语言连接 Redis 服务,并提供对应的操作 API

  • jar 包导入

    下载地址:https://mvnrepository.com/artifact/redis.clients/jedis

    基于 maven:

    <dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
    </dependency>
  • 客户端连接 Redis:API 文档 http://xetorthio.github.io/jedis/

    连接 redis:Jedis jedis = new Jedis("192.168.0.185", 6379)

    操作 redis:jedis.set("name", "seazean"); jedis.get("name")

    关闭 redis:jedis.close()

代码实现:

public class JedisTest {
public static void main(String[] args) {
//1.获取连接对象
Jedis jedis = new Jedis("192.168.2.185",6379);
//2.执行操作
jedis.set("age","39");
String hello = jedis.get("hello");
System.out.println(hello);
jedis.lpush("list1","a","b","c","d");
List<String> list1 = jedis.lrange("list1", 0, -1);
for (String s:list1 ) {
System.out.println(s);
}
jedis.sadd("set1","abc","abc","def","poi","cba");
Long len = jedis.scard("set1");
System.out.println(len);
//3.关闭连接
jedis.close();
}
}

工具类

连接池对象:

  • JedisPool:Jedis 提供的连接池技术
  • poolConfig:连接池配置对象
  • host:Redis 服务地址
  • port:Redis 服务端口号

JedisPool 的构造器如下:

public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port) {
this(poolConfig, host, port, 2000, (String)null, 0, (String)null);
}
  • 创建配置文件 redis.properties

    redis.maxTotal=50
    redis.maxIdel=10
    redis.host=192.168.2.185
    redis.port=6379
  • 工具类:

    public class JedisUtils {
    private static int maxTotal;
    private static int maxIdel;
    private static String host;
    private static int port;
    private static JedisPoolConfig jpc;
    private static JedisPool jp;

    static {
    ResourceBundle bundle = ResourceBundle.getBundle("redis");
    //最大连接数
    maxTotal = Integer.parseInt(bundle.getString("redis.maxTotal"));
    //活动连接数
    maxIdel = Integer.parseInt(bundle.getString("redis.maxIdel"));
    host = bundle.getString("redis.host");
    port = Integer.parseInt(bundle.getString("redis.port"));

    //Jedis连接配置
    jpc = new JedisPoolConfig();
    jpc.setMaxTotal(maxTotal);
    jpc.setMaxIdle(maxIdel);
    //连接池对象
    jp = new JedisPool(jpc, host, port);
    }

    //对外访问接口,提供jedis连接对象,连接从连接池获取
    public static Jedis getJedis() {
    return jp.getResource();
    }
    }