JDBC(Java Database Connectivity)Java数据库连接技术
官方JDK中提供的与JDBC有关的API都在java.sql包里面
API(Application programming Interface)
API是三方库中的一系列类和接口中方法的集合
调用API指的就是调用三方库中代码
Java客户端和数据库服务端的联系
Java与数据库连接的流程
需要让mysql驱动包的Driver类发生类加载即可(让类中的静态代码块立即执行)
Class.forName(String)这个方法是JDK提供的专门用于类加载的方法
参数写类的路径,必须是全路径:包名+类名
该方法会抛出一个编译时异常,ClassNotFoundException
代码如下:
Class.forName("com.mysql.jdbc.Driver")
DriverManager.getConnection()方法是用来建立client-server之间的TCP连接的方法有三个参数 1、url连接地址字符串 2、用户名 3、密码
url字符串格式
协议://IP:端口/数据库名称?参数名和参数值
1、(必须有)协议部分 "jdbc:mysql://"
Client与Server之间的通信协议是TCP/IP
传输的内容是协议是 jdbc:mysql://
2、(必须有)IP端口 "localhost:3306"
IP是服务器所在的机器的IP地址
服务端在本地可以写localhost,如果不在本地,必须写对方的IP地址
端口默认是3306
3、(必须有)数据库名称 "test"
mysql允许一个数据库服务端创建多个数据仓库
在建立CS连接的时候必须指定具体连接的仓库名称
如果指定的仓库名称不存在,连接会失败
4、(可选)连接参数 用 ? 拼接的部分
每一组参数都是kv结构,k = v,k是参数名,v是参数值
可以有多组,多组之间用 & 拼接
参数名 | 参数值 | 说明 |
useUnicode | true | 是否采用unicode编码集传输数据 如果不设置,默认是拉丁文,那么中文传输可能会乱码 |
characterEncoding | utf-8 | 使用unicode中的utf-8这个版本的字符集 |
useSSL |
false | 是否使用SSL证书进行安全验证 |
serverTimezone | 如果客户端所在城市与服务端所在的城市的时区不一样,可能会出现时间错乱的情况,可以通过这个参数来设置服务端的市区 |
我们在每一个测试的时候,都会注册驱动和连接数据库,那么如果每个测试都要连接的话很麻烦,我们可以这样做:弄一个静态代码块来类加载注册驱动,然后@Before单元测试方法完成数据库连接,然后用@After单元测试方法断开数据库连接,具体解释如下
1、先下载数据库三方库,这里我用的是mysql-connector-java 5.1.48版本,便于利用我在idea创建了一个下载数据库三方库的模板,直接快捷键就可下载
2、下载一个用来做单元测试的三方库JUnit,这里我用的是JUnit4.13版本,便于利用我在idea创建了一个下载JUnit三方库的模板,直接快捷键就可下载
3、安装数据库驱动jar包等前期工作准备完之后,我就开始了Java客户端与数据库的连接
//在程序运行过程中把只需要做一次的加载注册驱动的代码放到静态代码块里面
static {
try {
//让com.mysql.jdbc.Driver这个类发生类加载
//当类发生类加载的时候会自动执行static静态代码块
//注册驱动 = 决定当前JDBC接口的实现是mysql
Class.forName(DRIVER);//路径名:com.mysql.jdbc.Driver
System.out.println("mysql驱动注册成功!");
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("mysql驱动注册失败!");
}
}
@Before
public void before(){
try {
//DriverManager驱动管理员,getConnection()方法的底层把Socket封装好了
//建立数据库连接
conn = DriverManager.getConnection(URL,USER,PASSWORD);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Test
public void test01(){
//此时已经建立了数据库连接了
//insert语法:insert into 表名[(字段名列表)] values(值列表)
//[(字段名列表)]可以省略,如果省略的话,那么就代表全表插入
//可以通过指定字段名列表插入部分字段的值,没有插入的字段会插入null值,但是非空字段必须插入值
//values中值列表的个数个类型必须和字段名列表要一一匹配,(个数,类型)
//自增长的主键id,可以给一个null,自动增长
// String sql = "insert into user values(null,?,?,?,?)";
// String sql = "insert into user(name,sex,age) values(?,?,?)";
String sql = "insert into user values(null,?,?,?,?)";
try {
//预编译sql语句,返回一个执行对象ps
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1,"景甜");//把SQL语句中第1个?替换为"张飞"
ps.setObject(2,"女");//把SQL语句中第2个?替换为"男"
ps.setObject(3,35);//把SQL语句中第3个?替换为22
ps.setObject(4,"扬工院");//把SQL语句中第4个?替换为"南邮通达学院"
//把sql语句发送到服务端执行,并接受服务端返回的结果(服务端返回受影响的行数)
int row = ps.executeUpdate();
Assert.assertEquals(row,1);//断言row = 1
} catch (SQLException e) {
e.printStackTrace();
}
}
@Test
public void test02(){
//Update语法:update 表名 set 字段名 = 值 [,字段名=值...] where 条件
//单列更新、多列更新
//update 语句可以没有where条件,但是通常情况下都是要指定where条件防止全表更新
String sql = "update user set school = ?";
try {
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1,"南京信息工程大学");
// ps.setObject(2,2);
int row = ps.executeUpdate();
Assert.assertEquals(row>=1,true);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Test
public void test03(){
//Delete语法:delete from 表名 where 条件
//没有条件删除整张表
//delete 对表结构没有影响,删除指定的数据行
//truncate 会清空表中的所有行,但表结构及其约束,索引保持不变,自增长的主键会从头开始
//drow 会删除所有的表结构和所有行,包括表结构及其约束,索引等
String sql = "delete from user where id = ?";
try {
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1,1);
int row = ps.executeUpdate();
Assert.assertEquals(row >= 1,true);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Test
public void test04(){
//select 查询语法:select 列名列表 from 表名
//inner join / left join /right join 连接
//on 连接条件
//where 条件
//group by 分组字段
//having 分组的筛选条件
//limit 起始位置,往后的偏移量
//order by 排序字段
String sql = "select * from user";
try {
PreparedStatement ps = conn.prepareStatement(sql);
//增删改用executeUpdate()方法执行,返回int,代表受影响的行数
//查询用executeQuery()方法执行,返回ResultSet,代表查询的结果集
ResultSet rs = ps.executeQuery();
if (rs != null){ //如果查询不到任何数据,返回的结果集对象rs是null
while (rs.next()){ //判断结果中是否有下一行数据
// //根据表中的数据类型判断调用getXXX()方法
// //根据列的序号columnIndex作为参数进行取值
// int id = rs.getInt(1);
// String name = rs.getString(2);
// String sex = rs.getString(3);
// int age = rs.getInt(4);
// String school = rs.getString(5);
//根据列的名字columnLable作为参数来取值
int id = rs.getInt("id");
String name = rs.getString("name");
String sex = rs.getString("sex");
int age = rs.getInt("age");
String school = rs.getString("school");
System.out.println(id + "\t" +name + "\t" + sex + "\t" + age + "\t" + school);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
在此过程中涉及到的接口和方法 | |
接口/方法 | 作用 |
Class.forName("驱动类路径") | 加载驱动类,注册驱动 |
Connection conn = DriverManager.getConnection() | 创建数据库连接 |
PrepareStatement ps = 连接名.PrepareStatement(sql语句名) | 预编译sql语句 |
ps.setObject() | 设置sql语句中的 ?处的值 |
ps.executeUpdate() | 执行增删改操作,返回受影响行数 |
ps.executeQuery() ResultSet rs |
执行查询操作,返回结果集对象 用ResultSet rs接收 |
rs.next | 用于循环中,表示取出下一行数据 |
rs.getInt() | 取Int类型的值 |
rs.getString | 取String类型的值 |
rs.getDouble | 取Double类型的值 |
连接mysql服务器,进行权限验证
连接的过程中是多线程的,可以理解为多事务,同时并发的最大连接数 151
连接的形式是长连接,工作模式是半双工模式,客户端发送请求之后,服务端会返回请求的结果,也可以理解为客户端有请求服务端才有返回
对客户端发过来的sql语句进行分析,包括预处理和解析,生成“解析树”对sql语句的语法进行解析,有问题就会给客户抛出异常,对sql语句关键字进行比对,还会检查表名,字段名等是否合法
次阶段对sql语句进行优化,优化器会将这条sql语句转化为执行计划,比如查询的时候是否走索引,走哪条索引等等会生成多个执行方案,计算最佳的方案交给执行器执行
先进行权限认证,然后进入存储引擎打开数据表读取数据,重复执行计划中的操作
常用的存储引擎:myisam、innodb、memory
myisam的特点:拥有较高的插入,查询速度,但不支持事务
innodb的特点:mysql默认的存储引擎,支持事务
缓存是为了提高系统性能