JDBC(数据库的驱动、连接、java程序操作数据库、事务、隔离级别、连接池等)

java操作数据库的思想:连上数据库,发送sql语句。在连上数据库之前,要先用程序启动数据库,因此,可以通过反射加载类驱动(com.jdbc.mysql.Driver)。通过驱动管理类的静态方法传递数据库的url来获取一个连接对象(connection)。有三个重载的方法,第一个user和password都追加在url后(类似于get传参);第二种用逗号将user和passowrd隔开作为第二个和第三个参数;第三种通过配置文件Properties(方便修改,不用编译)。连接对象获取到表示数据库连接成功。
1.连接数据库。获得连接对象

public class Maintest {
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Properties p=new Properties();
        p.load(new FileInputStream("E:\\IDEAJAVA\\Algorithm\\src\\test\\jdbc.properties"));
        Connection connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/test",p);
        System.out.println(connection);
    }
}

输出结果

com.mysql.jdbc.JDBC4Connection@26a1ab54

2.得到连接对象后就可以通过连接对象(connection)获得发送sql语句的对象(statement和PrepareStatement)。PrepareStatement相比Statement,可以进行预处理。就是先发送SQL语句,后发送参数。发送查询语句用executeQuery方法,会返回一个Resultset对象。该对象封装了查询出的结果集;发送其他语句可以用executeUpdate方法。该方法返回一个int类型的值,是影响了数据库的条数。
建表前的数据库:

mysql> use test
Database changed
mysql> show tables;
Empty set (0.00 sec)

建表的语句:

Statement statement=connection.createStatement();//获得发送语句的对象
String createTable="create table student(" //创建表的sql语句
      + "id int not null auto_increment primary key,"
      + "name varchar(20),"
      + "sex varchar not default 'M'"
      + ");";
statement.executeUpdate(createTable);//执行sql语句

建表后在次查询收据库,表已建好。

mysql> show tables;
Empty set (0.00 sec)

mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| student        |
+----------------+
1 row in set (0.00 sec)

3.插入数据:运用Statement和PrepareStatem两种类。

String insertData1="insert into student values(1,'zhangsan','M')";//statement的sql
String insertData2="insert into student values(?,?,?)";
PreparedStatement preparedStatement=connection.prepareStatement(insertData2);
statement.executeUpdate(insertData1);//执行插入数据
//设置参数
preparedStatement.setInt(1,2);
preparedStatement.setString(2,"lisi");
preparedStatement.setString(3,"M");
System.out.println(preparedStatement.executeUpdate());//返回影响的行数

结果返回1。再看数据库中的内容,插入成功。

mysql> select * from student;
+----+----------+-----+
| id | name     | sex |
+----+----------+-----+
|  1 | zhangsan | M   |
|  2 | lisi     | M   |
+----+----------+-----+
2 rows in set (0.00 sec)

4.查询:通过发送sql的对象(Statement和PreparStatemente),用executeQuery方法发送查询的sql语句,返回Resultset对象。executeUpdate方法不能发送查询语句,可以发送建表,修改表结构,插入数据,删除数据,修改数据。

Statement statement=connection.createStatement();
String query="select * from student";
ResultSet resultSet=statement.executeQuery(query);//返回查询结果
while(resultSet.next())
{
    System.out.println("id:"+resultSet.getInt(1)+"\tname:"+resultSet.getString(2)+"\tsex:"+resultSet.getString(3));
}
System.out.println("--------------------------------------");
String querybyid="select * from student where id=?";
PreparedStatement preparedStatement=connection.prepareStatement(querybyid);
preparedStatement.setInt(1,1);//查询id为1的学生的信息。
ResultSet resultSet1=preparedStatement.executeQuery();
while(resultSet1.next())
{
    System.out.println("id:"+resultSet1.getInt(1)+"\tname:"+resultSet1.getString(2)+"\tsex:"+resultSet1.getString(3));
}

查询结果显示:

id:1	name:zhangsan	sex:M
id:2	name:lisi	sex:M
--------------------------------------
id:1	name:zhangsan	sex:M

5.批处理操作:通过addBatch方法向Statement对象中添加多个sql语句。再通过executeBatch方法执行每个SQL语句并返回每一条SQL语句执行后影响的行数。所以返回一个整型数组。但是批处理中不能放查询语句。

Statement statement=connection.createStatement();
statement.addBatch("insert into student values(3,\"luck\",\"F\");");
statement.addBatch("delete from student where id=1;");
System.out.println(Arrays.toString(statement.executeBatch()));

返回结果为1,1。数据库查询结果:

mysql> select * from student;
+----+------+-----+
| id | name | sex |
+----+------+-----+
|  2 | lisi | M   |
|  3 | luck | F   |
+----+------+-----+
2 rows in set (0.00 sec)

6.ResultSetMetaData类,该类封装了一个查询结果ResultSet的基本信息,比如多少列、类型、建议名等。

Statement statement=connection.createStatement();
ResultSet resultSet=statement.executeQuery("select * from student");
ResultSetMetaData resultSetMetaData=resultSet.getMetaData();
for(int i=1;i<=resultSetMetaData.getColumnCount();i++)
{
    System.out.print(resultSetMetaData.getColumnName(i)+"\t\t");
}
System.out.println();
while(resultSet.next())
{
    System.out.println(resultSet.getInt(1)+"\t\t"+resultSet.getString(2)+"\t\t"+resultSet.getString(3));
}

查询结果:

id		name		sex		
2		lisi		M
3		luck		F

7.事务:jdbc本身不支持事务,jdbc只是对事务做了简单的封装。还是用了数据库的事务。
事务四大特性:A(原子性)、C(一致性)、I(隔离性)、D(持久性)。
脏读:事务A读到了事务B修改单未提交的数据。
幻读:事务A第一次读到了符合条件的数据,事务B又添加了几条符合条件的数据,事务A再次读时,比第一次读到的多。出现幻读。
不可重复读:事务A读到数据后,事务B对数据进行修改,事务A再次读数据发现两次结果不一样。
针对以上三个问题出现以下四种隔离级别:
串行化(Serializable):级别最高,对事务串行执行,耗资源最大
重复读(repeatable read):保证了一个事务不会修改另一个事务已经读到的数据,避免了脏读和不可重复读。是大多数系统默认的隔离级别。
提交读(read commit):保证了一个事务不会读到一个已经修改但是还未提交的数据。只避免了脏读。
为提交读(read uncommitted):一个事务中的修改即使没有提交,其他事务也能查询到。一下是对事务的操作:

connection.setAutoCommit(false);//开启事务
try {
    statement.executeUpdate("insert into student values(4,\"abc\",\"M\");");
    statement.executeUpdate("delete from student where id=5;");//库中没有id=5的学生。出现异常
    connection.commit();//未出现异常时提交
} catch (SQLException e) {
    connection.rollback();//出现异常时撤回所有的操作
}finally {
    connection.close();
}

因为出现异常,所有撤回所有的操作,因此在表中不能查到“abc”。将删除的id改为2后继续执行,这次不会出现异常,因此两句都被执行并提交。

mysql> select * from student;
+----+------+-----+
| id | name | sex |
+----+------+-----+
|  3 | luck | F   |
|  4 | abc  | M   |
+----+------+-----+
2 rows in set (0.00 sec)

msyql支持四种隔离级别,隔离级别的查看与设置:
查看全局和会话事务的隔离级别

mysql> select @@global.tx_isolation,@@tx_isolation;
+-----------------------+-----------------+
| @@global.tx_isolation | @@tx_isolation  |
+-----------------------+-----------------+
| REPEATABLE-READ       | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.00 sec)

修改隔离级别:

mysql> set tx_isolation="serializable";
Query OK, 0 rows affected (0.11 sec)

mysql> select @@global.tx_isolation,@@tx_isolation;
+-----------------------+----------------+
| @@global.tx_isolation | @@tx_isolation |
+-----------------------+----------------+
| REPEATABLE-READ       | SERIALIZABLE   |
+-----------------------+----------------+
1 row in set (0.00 sec)

8.连接池:连接池内存放多个连接对象,当程序中需要用到时从中取出即可。不需要时,归还给连接池。实现连接对象的复用。连接池就是用来管理连接对象的。连接池要实现DataSource接口,该接口中有一个getConnection方法获得连接对象
c3p0连接池:下载c3p0的jar包,导入jar包。在src目录下加入c3p0.properties
配置文件。

public class Maintest {
    public static void main(String[] args) throws Exception {
        DataSource ds=new ComboPooledDataSource();
       for(int i=0;i<10;i++)
       {
           Connection con=ds.getConnection();
           System.out.println(con);
           con.close();//将连接对象归还给连接池
       }
    }
}

配置文件内容

c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql://localhost:3306/test
c3p0.user=root
c3p0.password=mysql
c3p0.maxPoolSize=10
c3p0.minPoolSize=3

Druid连接池:下载并导入jar包。Druid相比于c3p0来说,更加灵活。既可以通过配置文件来读连接数据库的信息,也可以通过程序来设定。

public class Maintest {
    public static void main(String[] args) throws Exception {
       DruidDataSource ds=new DruidDataSource();
       //通过程序设定
       ds.setDriverClassName("com.mysql.jdbc.Driver");
       ds.setUrl("jdbc:mysql://localhost:3306/test");
       //通过配置文件来设定。两种方式
//        ResourceBundle rs=ResourceBundle.getBundle("jdbc");
//        ds.setUsername(rs.getString("user"));
//        ds.setPassword(rs.getString("password"));
        Properties properties=new Properties();
        properties.load(new FileReader("E:\\IDEAJAVA\\Algorithm\\src\\jdbc.properties"));
        ds.setUsername(properties.getProperty("user"));
        ds.setPassword(properties.getProperty("password"));
        ds.setInitialSize(3);
        ds.setMaxActive(10);
        Connection con=ds.getConnection();
        System.out.println(con);
    }
}

以上代码建立了Druid连接池,通过程序设置了mysql的驱动,以及mysql的url。通过两种读取配置文件的方式来获取用户名和密码,再将其设置到连接池对象中。最后再通过程序设置连接池的初始容量以及最大个数。通过连接池获取对象,将对象输出。

你可能感兴趣的:(Java基础)