我们可以使用带有图形界面的客户端和数据库进行交互,然后我们在程序中如果需要数据库,显然这样是不行的。Java中访问数据库的方式主要是使用JDBC,JDBC是java规定的访问数据库的标准API,Java Database Connectivity,它定义了数据库的连接,SQL语句的执行以及查询结果集的遍历等。JDBC把这些操作定义为接口,位于包java.sql下面。如java.sql.Connection,java.sql.Statement,java.sql.ResultSet等。许多数据库厂商都在自己的JDBC驱动的实现了这些接口。如果Mysql的JDBC驱动mysql-connection-java.5.1.8-bin.jar。本博客主要是以mysql数据库为例来说明。
典型使用数据库操作是先要注册jdbc驱动,获取数据库连接,然后获取Statement对象,Statement对象用于执行SQL,获得的结果放在一个结果集ResultSet里,根据需要再进行抽取。
注册jdbc驱动有三种方式:DriverManager.registerDriver( new com.mysql.jdbc.Driver()); System.setProperty("jdbc.drivers","com.mysql.jdbc.Driver"); Class.forName("com.mysql.jdbc.Driver"); 推荐使用第一种和第三种方式,原因是第二种方式在编译时就需要加入相应的lib库,第三种方式最常用,移植性好,如果需要注册多个数据库驱动则推荐使用第二种方式。
获取数据库连接: Connection conn = DriverManager.getConnetion("jdbc:mysql://localhost:3306/test","root","admin");可以把对应的字符串写成String形式加入里面。然后就可以直接创建Statement对象进行操作了。在这几步操作中,其中在获取连接的时候比较慢,比较耗系统资源。使用jdbc之后资源一定要释放掉,释放资源的顺序是ResultSet,Statement,Connection。
如果我们执行sql语句,简单使用execute系列方法时,容易出现sql漏洞,让别人乘机进行sql注入方式非法获取数据,这种情况主要出现在查询的时候,因此在使用时常用到的PreparedStatement对象进行查询操作。只是在特定的驱动下,PreparedStatement做所有的操作才会更高效。它更加得灵活,该对象需要使用setXXX(,)来替换变量,第一个参数是位置序号,第二个才是值。
对于一些特殊的数据类型,比如时间,大文本等,jdbc也有相应的处理方式,数据库时间和Date类型转换使用以下方式: new java.sql.Date(birthday.getTimes())。如果我们要将一些大文本存储要用数据库中要使用text字段,我们在java程序中进行存取时可以通过字符流的方式读入,在取的时候使用Clob对象来储存getClob(),还可以把reader转换成String,通过ps.setString(),取的时候使用getString()。如下程序片段:
ps = conn.prepareStatement(sql);
reader.close();
而如果我们需要存取二进制文件,如果图片,当然我们需要使用image字段,在程序中我们使用Blob对象,同样的读入二进制流,获取时使用getBlob()。如下程序片段:
ps = conn.prepareStatement(sql);
在JDBC中也提供了事务管理的相关方法,我们可以手动打开事务;Connection.setAutoCommit(false),提交事务:Connection.commit(),回滚事务:Connection.rollback()。在mysql中有些存储引擎可能不支持事务,可将其改为InnoDB,通过show create table table_name来查看使用什么存储引擎。如果不需要完全回滚,则需要自己设置检查点,将事务回滚到检查点处便可。使用如下:SavePoint sp = conn.setSavepoint();
if( conn!=null && sp!=null){
conn.rollback(sp); conn.commit();
}
对于跨数据库的事务,则简单使用jdbc是不行的,这样使用jpa规范。
使用jdbc还是设置数据库的隔离级别,conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED),对于mysql数据库可通过 select @@tx_isolation 查看隔离级别,并通过命令 set transaction isolation level XXXXXX手动设置隔离级别。其实四种隔离级别只是在加锁和释放锁的时候,以及读锁写锁的不同而已,在读未提交时读操作不加S锁,而在读提交下读操作加了S锁,但是在语句执行之后就释放掉了,可重复读时读操作加了S锁,只是在事务结束之后才释放掉S锁,在最高级别可串行化时,又添了一个范围锁,保证一个事务之内两次查询结果一致。
在有些时候我们需要查询之后获得主键,比如用户在注册之后,应该让其直接登录。使用PreparedStatement.getGeneratedKeys(),要是在创建Statement对象时使用Statement.RETURN_GENERATED_KEYS常量,通过使用TYPE_SCROLL_INSENSITIVE,创建可以滚动的结果集,使用CONCUR_UPDATABLE创建可以更新的结果集。我们还可以使用PreparedStatement进行批处理SQL,可以大幅度提升增、删、改的速度,使用方法有ps.addBatch(),ps.executeBatch()。
在mysql中我们使用limit来实现分页, 如select * from person limit 21,10 ,只取出从第21行开始10行记录。jdbc还提供了获取元数据信息的对象,ResultSetMetaData,DatabaseMetaData,通过前者我们可以返回结果集元数据,遍历元数据即可获取ResultSet中有哪些列,每一列是什么类型,甚至是列长度等,我们可以根据需要获取相应的信息。如果我们事先并不知道列的名字,使用结果集元数据显然很方面。后者我们可以获取数据库的一些基本信息。
jdbc在web应用中一般用在数据访问层,而我们通常在程序经常遇到是需要将javaBean存放在数据库中,于是许多厂家把jdbc再次进行包装就出现好多的框架。而我们具体是使用哪种实现,一般通过Factory模式,具体使用哪种实现,我们可以将实现类放入文件中,通过反射技术建立Dao接口的实现方式,这样将具体实现分开更换时方便,更符合软件设计原则。
jdbc中也为我们提供了一个标准的数据源接口,DataSource接口。取代DriverManager来获取Connection,这样速度更快。连接池中缓存Connection,不至于程序不断建立和释放连接,这样更高效,而我们的程序只需要和Datasource打交道就可以同样参与数据的存取。 开源的数据源实现DBCP,需要我们配置参数,其使用BasicDataSourceFactory.createDataSource()。