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。通过两种读取配置文件的方式来获取用户名和密码,再将其设置到连接池对象中。最后再通过程序设置连接池的初始容量以及最大个数。通过连接池获取对象,将对象输出。