众所周知,mysql 的 jdbc 在代码中只需要这样写就能实现数据框的连接:
private static final String URL = “jdbc:mysql://127.0.0.1:3306/bill?characterEncoding=utf-8”;
private static final String USER = “root”;
private static final String PASSWORD = “123456”;
//1.加载驱动
Class.forName(“com.mysql.jdbc.Driver”);
//2.获取链接
Connection conn = (Connection) DriverManager.getConnection(URL, USER, PASSWORD);
//3.执行sql
Statement st = (Statement) conn.createStatement();
ResultSet rs = st.executeQuery(“select * from user”);
//4.处理返回结果
while(rs.next()){
System.out.println(rs.getString(“某字段”));
}
//5.关闭资源
rs.close();
st.close();
conn.close();
只需要这五步,但是有没有小伙伴很好奇这里面究竟都干了些什么呢?今天我们就来探究一下吧。
- 一) 驱动加载:Class.forName(“com.mysql.jdbc.Driver”);
我们从第一行加载驱动来看
- 1.1)Class.forName(全类名)
这段代码其实就是告诉JVM,你可以准备我了,让‘我’投入你的怀抱:类加载
(类加载不会创建实例,只会执行静态块中的东东)
我们不妨进来看看里面都有什么:
哈哈,就是这么简单粗暴。
他就是想将 com.mysql.jdbc.Driver(mySql驱动) 这个类,
注册到 java.sql.DriverManager(java API 驱动管理)类里面而已。他不说我也能猜到,其内部必然有一个集合或数组来存放我们的mysql驱动的。
于是我们接着看一下这个驱动管理究竟干了些什么!
我呸,果然是个心机boy,他就是把我们的mysqlDriver穿了一个马甲,变成了DriverInfo!
好吧,就算他穿了马甲,我们也是能一眼就看出他的本质,仍然是一个mysql驱动。
紧接着,他就把穿了马甲的驱动放到了一个很洋气的集合中,这个集合是不是看着很眼熟!
不需要他说话,我们大致能从他的尾巴出看出来,这货其内部一定是用数组实现的,百分之八十跟ArrayList有一腿。于是乎我们把它的衣服稍稍的掀开一角,看看它内部究竟是个啥。
嘿嘿,当把它简单粗暴的扒开后,当然是看重点了,什么大腿啊。。。
原来,它内部每次添加什么东西就会直接新建个比原来容量大一的一个新数组,其余跟ArrayList没差多少!
这是什么情况!他不用担心线程安全问题嘛!!!
这个数据结构是需要担心线程安全的,但是小伙伴看一下添加数据到集合中的时候,人家已经对安全问题负责了呢,他已经自觉的戴上了synchronized牌的套套!
这不禁让人思考,为什么不直接用arrayList 而是要用这个集合呢?
我大胆的猜测了一下,arrayList集合初始化的时候就有一定容量(好像是10个吧),而数据库驱动也就那么几种嘛(sqlServer、mysql、Oracle…请原谅楼主关系数据库就玩过这么几种),如果一下申请了那么大的空间不是很浪费?于是乎我从侧面发现了一个小细节!设计驱动接口API的小哥一定是个持家好手!
于是乎,我们在用jdbc的时候,记得不要每连接一次数据库就摸人家一把,而是第一次摸完了就应该收敛点,不然人家小哥辛辛苦苦操持家业,你怎么对得起人家!(单例模式就不在此介绍了,网上有好多,本文文尾也有一个简单的小例子,把注册做成了单利)
- 1.2)获取连接
Connection conn = (Connection) DriverManager.getConnection(URL, USER, PASSWORD);
刚刚已经毁了人家的清白,那就娶了吧!真汉子怎么可以始乱终弃!
封好聘礼(properties info)骑上高头大马getConnection(。。。),带上媒婆 url 走起!
(别问我Reflection.getCallerClass()这是个什么鬼,我只在类反射的时候才会抱他大腿,对仗有木有很公正,哈哈哈,想了解这个东西看最下面补充有介绍)
你要问我骑得什么马,我会告诉你,神马神马!
滴,遍历所有驱动去尝试对连接进行碰撞,直到遇上对的人!好黄,好暴力有木有!
划重点!这时候是调用刚刚注册进来的mysql_Driver!
是mysql老爹的亲儿子!com.mysql.jdbc.Driver
是mysql老爹的亲儿子!com.mysql.jdbc.Driver
是mysql老爹的亲儿子!com.mysql.jdbc.Driver
重要的事情说三遍!
我立刻到Driver包里去找connect()!难道是我眼花了!我刚刚注册时候分明只有一个构造方法,加一个静态块,哪里还有别的鬼!Driver接口里是指不上能有实现了,那必然就是在其基类老干爹NonRegisteringDriver中!
老干爹不负众望,果然实现了这个方法。到此打住,我并不想继续探索老干爹有几房姨太太,我只想知道,老干爹能帮他儿子把链接给我搭上,并且交给了我。(这里我不想说多态里面的上、下溯造型,感兴趣的可以看一下《java编程思想》或者百度,反正我忘记我是在哪里了解到的了~)
1.3)执行语句
Statement st = (Statement) conn.createStatement();
ResultSet rs = st.executeQuery("select * from user where username = ‘s’ and password = ‘嘿嘿嘿’ " );
Or
PreparedStatement preSta = (PreparedStatement) conn.prepareStatement("select * from user where username = ? and password = ? ", new String[]{“s”, “嘿嘿嘿”});
ResultSet rs = preSta.executeQuery();
形式就不多说了,一个是占位符填充条件,一个不是,就这样~。
其过程呢就好像娶了老婆要生娃是一个道理~
执行sql语句,得到你想要的数据
最后记得关闭资源,不要问为什么。就好像不能让不拉屎的一直占着茅坑是一个道理。数据库连接资源也是有限的,就那么几个蹲位。。。
楼主顺手写了一个单例给新报道的小伙伴,另外楼主没有很好的封装内部,只是把加载驱动做了一个单例而已(尽管多注册几个也没什么关系,谁还不是小公举呢,我就不!)
//连接url
private static final String URL = "jdbc:mysql://127.0.0.1:3306/bill?characterEncoding=utf-8";
//数据库管理员账号
private static final String USER = "root";
//数据库密码
private static final String PASSWORD = "123456";
//单例Dao
private static BaseDao baseDao = null;
//私有构造方法,不然在外面new出来了还单例个毛线
private BaseDao(){ }
//在这里获取我们最想要的小东东
public static BaseDao initBaseDao(){
if (baseDao == null){
synchronized(BaseDao.class) {
if (baseDao == null){
try {
//1.加载驱动程序(这三种写法,虽然看起来不同,实际功能确是一样,随便选哪个都行,没错是选一个!因为介个现在实行一夫一妻制嘛,嘿嘿嘿!)
Class.forName("com.mysql.jdbc.Driver");
// new Driver();//com.mysql.jdbc.Driver
// java.sql.DriverManager.registerDriver(new Driver());
//划重点 虽然他们的功能是一样的,但是意义却略有差别
//第一种:加载类Driver到JVM中并立即执行静态块,注册驱动,实例化Driver类
//第二种:加载类Driver到JVM中并立即执行静态块,注册驱动,第一次实例化Driver类,第二次实例化Driver类
//第三种:第一次注册驱动,加载类Driver到JVM中并立即执行静态块,第二次注册驱动,第一次实例化Driver类,第二次实例化Driver类
//所以说这个类的设计者真的是大牛,他直接就是让使用者用Class.forName("com.mysql.jdbc.Driver");这一行代码来注册驱动,让整个注册过程完美到了极点,没有一丝一毫的赘肉!(如果看这三行注释有阻碍的童鞋,先看最下面补充部分的类执行顺序)
} catch (Exception e) {
e.printStackTrace();
}
baseDao = new BaseDao();
}
}
}
return baseDao;
}
public void index(){
try {
// com.mysql.jdbc.ConnectionImpl
//2.获得数据库链接
Connection conn = (Connection) DriverManager.getConnection(URL, USER, PASSWORD);
//3.通过数据库的连接操作数据库,实现增删改查(使用Statement类)
Statement st = (Statement) conn.createStatement();
ResultSet rs = st.executeQuery("select * from user");
// PreparedStatement preSta = (PreparedStatement) conn.prepareStatement("select * from user where username = ? and password = ? ",
// new String[]{"s", "嘿嘿嘿"});
// ResultSet rs = preSta.executeQuery();
//4.处理数据库的返回结果(使用ResultSet类)
while(rs.next()){
System.out.println(rs.getString("name"));
}
//关闭资源
rs.close();
st.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
补充:类中代码执行前后顺序
静态成员变量
静态块(只在类加载时的最后一步:初始化的时候执行一次)
非静态成员变量
非静态块(每次被实例化都会执行一次)
构造方法
Class.forName(“全类名”) 等价于 Class<?> clazz = Class.forName(“全类名”, true, 类.class.getClassLoader());
即为初始化类的静态块、静态成员变量
Class<?> clazz = Class.forName(“全类名”, false, 类.class.getClassLoader());
即为不初始化类的静态块、静态成员变量
但是该类在第一次被实例化的时候,会自动初始化类的静态块、静态成员变量。
这个初始化的过程仅仅执行一次:或是在类加载的时候,或是在第一次实例化的时候!