引入:
先猜想一下 java程序如何和mysql建立连接:思路:可以用socket,需要遵循的是mysql协议。那么mysql又是如何规定发送的数据的格式和mysql返回的响应数据的格式呢?
java程序通过mysql协议发送请求(内容以sql语句)给服务端,服务端按照mysql协议处理数据结果,
java程序再解析协议,处理数据(这些过程mysql已经给我们提供了一些API)
但是又出现了一个问题:不同的数据库的协议不同,那么API也不会不同,那我们的java程序就得重写去适配不同的数据库。
此时sun公司指定了一套标准协议,所有支持java语言的数据库,必须支持java语言的一套协议:jdbc
JDBC:java database connectivity
也就是数据库和java程序交互的一套标准API。但是这些API,大多都是接口,我们还必须实现?
这些具体实现,都通过不同的数据库厂商自己来实现,也就是 数据库的驱动程序。
所以虽然实现不同,但是都是相同的接口。
驱动程序: 实现mysql协议的jar包
所以jdbc的流程就清晰了:
1.建立连接
2.书写sql语句,mysql服务器发送sql语句
3.mysql接收并按照sql语句来处理数据
4.mysql服务器发送响应到客户端,客户端接收数据
5.java程序按照业务逻辑,处理响应数据。
6.释放资源。
测试框架:
对public class 类名,的类名右键,Goto ——> Test ——>JUnit4(开源测试框架)
里面的所有方法都可以单独运行。
一旦添加过这个JUnit4框架,以后就直接在普通类中的 要运行的方法 添加注解:@Test
编写JDBC程序:
1.向JVM注册驱动:注册的是具体的数据库开发厂商实现的Driver类对象
DriverManager.registerDriver(new Driver());(这个Driver是mysql的)
2.建立连接(数据库jdbc和java程序):
String url="jdbc:mysql://localhost:3306/jdbc"
url:要连接哪个数据库,格式: jdbc:mysql(主协议):[子协议(可有可无)] //localhost:3306(主机端口号)/test(数据库名称)(?参数名=参数值)
String user="root"
连接数据库的用户名
String password="1234"
密码
Connection connection = DriverManager.getConnection(url,user,password)
引用表示java和mysql服务器的连接。
3.写sql语句,将请求发送给服务器。
String sql ="insert into user(id,name,age) values(1,'zs',18)"
Statement statement = connection.createStatement();
//增/删/改(DML语句)的时候使用,返回值 int i 代表 本次操作所影响的行数。
int i = statement.excuteUpdate(sql);
//查(DQL语句)
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
//查找数据表的属性值
1.通过列名获取每个属性
resultSet.getInt("id");
resultSet.getString("name");
2.通过列名的index索引来获取(index 从1 开始编号,表示列数)
resultSet.getInt(1);
}
4.根据业务逻辑处理响应数据。
if(i>0){
sout("插入成功")
}
5.释放相关资源
//如果只有增删改操作的话:
statement.close();
connection.close();
程序详解:
1.驱动的加载:
DriverManager.registerDriver(new Driver());查看mysql的源码发现,Driver类自己已经有了静态方法:Static { DriverManager.registerDriver(new Driver());}
所以我们就不需要重复书写这句代码,只要确保这句代码在我们自己的程序执行之前执行就可以了。
但是如何做呢?注意到 此代码是Driver类的静态代码块中的,又因为静态代码块在类加载的时候执行。
那么有几种方式类加载:
1.生成对象:DriverManager.registerDriver(new Driver());//需要导mysql.Driver包。
2.Class.forName("com.mysql.jdbc.Driver") 全类名(推荐)
3.放在程序的静态代码块中,statice{Class.forName("com.mysql.jdbc.Driver")}
4.坑,不会加载静态代码块的方法:driver.class()
如果我想要跨数据库来加载(不同的驱动都可以运行) : 通过配置文件。
2.建立连接
方式一:设置好url,user,password
Connection connection DriverManager.getConnection(url,user,password);
方式二:
原url:String url="jdbc:mysql://localhost:3306/jdbc"
灵活使用url(user/password通过参数给) : String url="jdbc:mysql://localhost:3306/jdbc?user=root&password=****"
如果默认IP地址是localhost、端口号是3306,则简写:String url="jdbc:mysql:///jbdc?...."
方式三:
配置文件:在 src 目录下new 一个配置文件(configure)。
url="jdbc:mysql://localhost:3306/jdbc"
user=""
password=""
在类中:
把配置文件当Map<String,String>用
Properties properties = new Properties();
new FileInputStream("相对路径")
那么相对路径是什么?sout(System.getProperty("user.dir"))
相对路径会改动,如果启动服务器的话,所以行不通
获取配置文件:
法一:URL resouce = 类名.class.getClassLoader().getResource("配置文件名")
这个路径 是 相对于 src的路径(虽然其的路径是到了classes目录下,但是是把配置文件复制到classes目录下的)
法二:
(通过反射拿到配置文件,放入输入流中)
InputStream RS = 类名.class.getClassLoader().getResourceAsStream("配置文件名");
//加载配置文件
properties.load(RS);
//获取Driver的全类名:在配置文件中 : URL=com.mysql.jdbc.Driver
String url = properties.getProperty("URL");
//建立连接
Connection connection = DriverManager.getConnection(url,properties);
3.写sql语句,发送给服务器。
1.获取statement对象Statement statement = connection.createStatement();
2.ResultSet — 结果集(也是一种需要释放的资源)
ResultSet resultSet = statement.executeQuery(sql);
ResultSet 结果集 中会维护一个游标,用游标来指示表的具体行。
遍历 ResultSet 结果集:
先将游标向后移动一个位置,如果游标的当前位置是一个有效的位置(表内)就返回true
(无效位置就是表的第一行数据之前和最后一行数据之后。)
//开始的时候游标处在第一行数据之前的位置
while(resultSet.next()){ //.next()结果返回true或false
//遍历每一行,获取其每一个属性值
//两种方式获取当前行的各个属性值
1.通过列名获取(游标所指的行)
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
2.通过列的 index索引值 来获取(不太推荐,可读性差)
index 从第一列开始编号.
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
int age = resultSet.getInt(3);
}
游标的位置还可以通过API进行修改:
resultSet.afterLast() ——将游标放在表中最后一行之后
resultSet.BeforeFrist() ——将游标放在表中第一行之前
absolute(int row) ——将游标放在指定行(从1开始),要注意遍历的时候,游标会先移动一位
游标的前后数据判断:(返回的都是boolean类型)
resultSet.next()/resultSet.previous()
一个注意点(注意区分):
//查询操作,返回的是一个结果集
ResultSet resultSet = statement.executeQuery(sql);
// 增 删 改 操作,返回的是一个修改了多少行的数值
int i = statement.executeUpdate(sql);
3.根据业务逻辑处理响应
1.引入 TestCase ,让测试框架根据我们的业务逻辑来判断测试成功与否TestCase.assertTrue(i > 0);(这里i是上面的修改数据表的行数)
执行条件表达式如果为false,会直接抛出异常。
2.测试框架中异常的处理:
如果测试框架中,自己捕获了异常,那么测试框架就会忽略这个异常,继续执行。
4.释放资源
因为Statement/Connection/ResultSet 都继承了 AutoCloseable,所以他们的释放可以写一个方法来释放,而不用在主代码中写:if(st != null){st.close()}if(ct != null){ct.close()}....
public static void release(Statement st,Result rs,Connection con){
closeQuietly.st;
closeQuietly.rs;
closeQuietly.con;
}
private static void closeQuietly(AutoCloseable closeable){
//因为Statement/Connection/ResultSet 都继承了 AutoCloseable
if(closeable != null){
closeable.close();
}
}
发现上面的代码有很多重复的代码,自己创建一个工具类DBUtil:
public class DBUtil{1.注册驱动
static {
2.建立链接
//将全类名放入配置文件 class="com.mysql.jdbc.Driver"
Properties properties = new Properties();
InputStream configureStream = 类名.class.getClassLoader().getResourceAsStream("配置文件名.properties");
//加载配置文件
properties.load(configureStream);
//从配置文件中拿东西
Class.forName("com.mysql.jdbc.Driver")}
2.public static Connection getConnection(){ //这里的Connection对象最好导java.sql.Co