JDBC连接数据库增删改查(JavaBean,自定义工具类,配置文件)(四)

383 阅读7分钟

JDBC数据库连接

close()的必要性:

如Java中IO如果不释放的话,如果有死循环,文件资源一直被占用,就文件打不开也删不掉。

JDBC使用网络来和数据库连接,如果不释放的话,很容易出现IO这种占用的情况,网络会变得拥挤卡顿,执行其他连接的时候效率会变低。

为什么在JDK的API中没有找到直接访问mysql数据库的接口的实现类呢?

因为市面上并不只有mysql,还有Oracle等别的数据库,Java并不清楚这些数据库的内部结构,不能直接对数据库操作。所以只提供了接口以及驱动管理类,它要求这些数据库厂商提供自己数据库的驱动,这些驱动其实就是接口的所有实现类的字节码文件的压缩包(只有.class没有.java)。

JDK提供的DriverManager类来将驱动加载进来,使得我们可以使用相关的类来操作数据库。同时,由于我们是通过网络来连接数据库的,所以在正式进行操作前还要获取一个数据库连接。

连接数据库时使用了网络?使用这个网络时我们用的时什么协议呢?

tcp协议,也就是说mysql实现了一个类似于ServerSocket的功能,作为服务器,当我们在系统服务中关掉MySQL57服务后,这个服务器就关闭了,这个时候连接一定失败。

实现步骤

(如果项目中没有添加驱动,那么要先添加数据库的驱动程序mysql-connector-java-5.1.46.jar)

开发步骤:

1 注册驱动程序

//方法1
DriverManager.registerManager(new Driver());
    //这种方式占用内存,这个驱动会被注册两次,另一次是数据库厂商提供的Driver类中的静态代码块执行的。
    //我们可以使用类的反射来优化。如下://方法2(推荐)
Class.forName("com.mysql.jdbc.Driver");
//jdk1.6以后可以不用写,jdk自动注册。
//但是建议记住,以后使用连接池的时候是必定要写的。
    

这里括号里的Driver类是mysql-connector-java-5.1.46.jar提供的Driver接口的实现类

2 获取数据库连接对象(java.sql.Connection)

使用的是驱动管理类DriverManager的静态方法getConnection(String url,String user,String password)

参数:

url:连接数据库的网络地址

连接协议:数据库厂商名://数据库的IP地址:数据库的端口号/连接的数据库的名字。例如:

mysql:localhost://3306/mydb

user:用户名

password:密码

static getConnection()方法,返回Connection接口的实现类对象(数据库的连接对象)。

String url = "mysql:localhost://3306/mydb";
String username = "root";
String password = "123456";
Connection conn = Driver.getConnection(url,username,password);

3 执行SQL语句的执行对象

执行SQL对象:java.sql.Statement

通过连接对象conn的方法获取

方法:createStatement()方法,获取SQL语句的执行对象,Statement CreateStatement()返回值,返回Statement对象。

Statement stat = conn.createStatement();

4 (如果要查询的话有这一步)Statement对象执行SQL语句

使用stat对象的方法,executeUpdate(String sql),,返回值是int类型,数据操作影响的行数>0,那么操作成功。

这个返回值就是数据库中进行操作后,提示的影响了xx行的这个xx值。

String sql = "insert into product values(值)";
int row = stat.executeUpdate(sql);

这里插入数据库后,数据库和java的编码可能不同,导致这行会乱码

建议将url改成

mysql:localhost://3306/mydb??characterEncoding=库中的编码

5 获取数据库查询的结果集对象,

结果集:java.sql.ResultSet

//执行查询
String sql = "select * from product";
ResultSet result = stat.executeQuery(sql);
//可以发现,查询和更新(插入)的方法不同
//在遍历result前要先判断下是否为空。否则的话有抛出空异常的可能。
//这个result有一个类型于迭代器hasnext方法的next(),遍历可以用它的next()
//取出结果集 :rs对象方法 getXXX(String 列名) 这个XXX就是这列数据的数据类型
//varchar对应String
​
List<Product> list = new ArrayList<Product>();
while(result.next()){
    int pid = rs.getInt("pid");
    String pname = rs.getString("pname");
    double price = rs.getDouble("price");
    int num = rs.getInt("num");
    int cno = rs.getInt("cno");
    System.out.println(pid +","+pname+","+price+","+num+","+cno);
    
    //当我们拥有这个表的JavaBean时,可以直接放进对象属性中,然后把这个对象放进集合里
    Product p = new Prodect(rs.getInt("pid"));
    p.setId(rs.getString("pname"));
    p.setPname(rs.getString("pname"));
    p.setPrice(rs.getDouble("price"));
    p.setNum(rs.getInt("num"));
    p.setCno(rs.getInt("cno"));
    list.add(p);
}
//遍历集合,输出对象toString,注意判断下集合是否为空,此处不加时,也不会出现空指针,只是为了效率方面考虑,for没有if判断来的快。
if(!list.isEmpty){
    for(Product p : list){
        System.out.println(p);
    }
}

在遍历这个结果集时可以发现有些列是null值,但是getInt返回的这个值是0.

如果是getObject才会返回一致的null,但是尽量不要用,很不负责,这不符合它本身的数据类型。

6 释放资源

result.close();
stat.close();
conn.close();

自定义的JavaBean要求

必须要有的:成员变量私有化,get/set方法,无参构造方法,实现序列化接口。

提取重复代码装到自定义工具类中

这样做,能够减少代码的冗余。

1 可以将加载驱动,读取配置文件放入静态代码块中(因为只执行一次)

2 获取连接,放到返回值为Connection的静态方法中(因为有返回值)

3 关闭资源,放到关闭的静态方法中去,注意判断一下三个资源是不是null,try..catch异常。

注意:

要将四大属性的声明在成员变量里,因为静态代码取出后要赋值给它,但是静态代码没有返回值,局部变量的话传递不出来,而获取连接要使用它。尽量不要使用final 修饰,因为final的话一旦声明就要给它初始值(编译时异常),而且也不能随便给初始值,因为一旦静态代码块中发生了异常,没有找到资源文件或者没有找到属性,那么程序会按照初始化的值进行下去而不是停止。可以声明为private static String 类型。

配置文件的位置

为什么要把配置文件单独放?

如果直接将username,password,url,driver信息直接写到代码中的话,它修改起来非常麻烦,放进.properties文件后,就非常清楚明了,还有就是,提高了代码的安全性,可以只给使用者修改配置文件就行,不用直接在代码上改动。

放在项目的src目录下,java文件编译后会在.out目录下生成.class,其实不止.java,只要在src项目下,最后都会到.out中去。这样做有一个好处,在项目上线时是不会把源码放进项目去的,配置文件和.class都在一块,避免了文件移动的麻烦。

读取配置文件的原理:

读取配置信息dbconfig.properties获取数据库连接的四大信息,连接信息没有变化,读取一次即可,我们可以使用静态代码块来实现。

类加载器ClassLoader:将类加载到内存,并且创建字节码文件的对象。

思路

我们可以使用类加载器的getResourceAsStream方法返回的流来加载配置文件,之所以使用这个流而不使用新创建的IO流,是因为新的IO流需要手动关闭,IO传入绝对路径,而且文件的绝对路径写着不方便;而类加载器的这个流生命周期跟类加载器相关,类加载器加载完了,这个类加载器也就没有用了,这个流也就跟着关闭了,当然,我们也可以手动将他提前关闭。

Application ClassLoader:加载自定义的类,jar包的类

BootStrap ClassLoader:加载JDK的核心类库,String,Object,集合,IO

Extension ClassLoader:(用的少)加载扩展类库

加载器类ClassLoader方法:InputStream getResourceAsStream(String 文件名) 返回字节输入流。

getResourceAsStream()会自动从源码的根目录下扫描文件,就是src目录。

读配置文件(四大属性)

private static String driver;
private static String url;
private static String username;
private static String password;
​
static{
try{
    InputStream input = 自定义工具类.class.getClassLoader().getResourceAsStream("dbconfig.properties");
//定义集合
Properties prop = new Properties();
//流对象读取到配置文件的数据,存储在集合里
prop.load(input);
//从集合中取出数据
 driver = prop.getProperty("driver");
    url = prop.getProperty("url");
 username = prop.getProperty("username");
     password = prop.getProperty("password");
   }
    //注册驱动
    class.forName(driver);
   //这里不能返回值,所以写获得连接是无效的。需要写在方法里,而且由于这个方法要用到这几个属性,而方法又和静态代码块在一个类,可以将他们作为静态成员变量声明,然后静态代码块调用后赋值即可。
​
}catch(Exception e){}
​