Java连接数据库-JDBC(Java DataBase Connectivity):为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
一、JDBC使用步骤
1.加载驱动:以使用MYSQL为例
Class.forName("com.mysql.jdbc.Driver");
2.创建连接:
Connection con=DriveManager.getConnection("jdbc:mysql://localhost:3306/数据库名称?characterEncoding=utf-8","username","password");
3.创建执行SQL语句的对象(Statement/PreparedStatement):
1)Statement:
Statement stmt=con.createStatement();
存在问题:注入攻击
用户输入数据中注入非法的 SQL 语句段或命令,从而利用系统的 SQL 引擎完成恶意行为的做法
解决方法:使用PreparedStatement就可以了。而且使用PreparedStatement代码的可读性和可维护性比较好
2)PreparedStatement:PreparedStatement是Statement的子接口,它表示一条预编译过的SQL语句PreparedStatement对象所代表的SQL语句中的参数用问号(?)来表示,调用 PreparedStatement对象的setXXX()方法来设置这些参数。set方法有两个参数,第一个参数是要设置的SQL语句中的参数的索引(从1开始),第二个是设置的SQL语句中的参数的值
因为PreparedStatement是预编译的,所以在多次执行相同的sql语句时速度更快;
//PreparedStatement对SQL语句先预编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。
String sql = "insert into student(sno,sname,sex,height,tel) "+ "values(default,?,?,?,?)";
stmt = conn.prepareStatement(sql);
stmt.setString(2, name);
stmt.setString(3, sex);
stmt.setDouble(4, height);
stmt.setString(5, tel);
stmt.execute();
4.执行SQL语句,得到结果:
int rs=stmt.executeUpdate(sql); //增删改
Resultset re=stme.executeQuery(sql);//查
【注】:PreparedStatement要调用无参数的execute()!!!!!Statement调用有参数的execute(sql)。如果调用PreparedStatement.execute(sql),就白预编译和设置参数了,执行的还是Statement中的execute(sql),可能还会报错。
5.关闭连接:
con.close();
stmt.close();
二、ResultSet
数据库结果集的数据表,通常通过执行查询数据库的语句生成。ResultSet对象具有指向其当前数据行的指针,最初指针被置于第一行之前,next()方法将指针移动到下一行。
其他常用方法:
boolean next():将指针移动到下一行,如果下一行没有记录返回False,否则返回True。
void afterLast():将指针移动到此 ResultSet 对象的末尾,正好位于最后一行之后
boolean last():将指针移动到此 ResultSet 对象的最后一行。
void beforeFirst():将指针移动到此 ResultSet 对象的开头,正好位于第一行之前。
boolean first():将指针移动到此 ResultSet 对象的第一行。
int getXxx(int columnIndex) :以Xxx的形式检索此ResultSet对象的当前行中指定列的值。
int getXxx(String columnName): 以Xxx的形式检索此ResultSet对象的当前行中指定列的值。
int getRow() :检索当前行编号
ResultSetMetaData getMetaData() :检索此 ResultSet 对象的列的编号、类型和属性
三、ResultSetMetaData(结果集元数据)
可用于获取关于ResultSet结果集中列的类型、列名或其他属性信息,存放在ResultSet对象中,因此可以通过re.getMetaData()获得
常用方法:
int getColumnCount():返回此 ResultSet 对象中的列数。
String getColumnName(int column):获取指定列的名称,索引从1开始
String getColumnLabel(int column):获取指定列的别名,索引从1开始
int getColumnType(int column):检索指定列的类型名称
String getColumnTypeName(int column):检索指定列的数据库特定的类型名称。
int getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位。
String getTableName(int column):获取指定列所在表的名称。
String getCatalogName(int column):获取指定列所在数据库名称。
四、DatabaseMetaData
关于数据库的整体综合信息。
DataBaseMetaData对象是在Connection对象上调用getMetaData获得的。
常用方法:
String getDatabaseProductName() 检索此数据库产品的名称
String getDatabaseProductVersion() 检索此数据库产品的版本号。
int getDatabaseMajorVersion() 检索底层数据库的主版本号。
int getDatabaseMinorVersion() 检索底层数据库的次版本号。
五、多条SQL语句批处理
注意:批处理不同于事务,区分二者的不同。
①Statement
Statement st=conn.createStatement();
st.addBatch(sql1);
st.addBatch(sql2);
st.addBatch(sql3);
st.executeBatch();
②PreparedStatement:只适用于结构相似的语句之间做批处理
PreparedStatement pst=con.preparedStatement(sql1);
for(int i=0;i<100;i++){
pst.setInt(1,i);
....
pst.addBatch();
}
pst.executeBatch();
六、JDBC的事务控制
通过Connection来控制事务
conn.setAutoCommit(false); //取消自动提交事务,手动提交
......
......
conn.commit(); //提交事务
conn.rollback(); //回滚事务
七、日期类型的处理
mysql中表示日期的类型有三个:Date,Time,Timestamp。Data只能表示年月日,Time只能表示时分秒,Timestamp不仅可以表示年月日时分秒,还可以表示到更精确。
java中有同样三个日期类型与之相对应,这三个类型位于java.sql包中。并且这三个类都是java.util.Date的子类。
八、获得数据库自动生成的主键
在创建PreparedStatement时,指定一个参数autoGeneratedKeys,其中autoGeneratedKeys的值为Statement.RETURN_GENERATED_KEYS或者Statement.NO_GENERATED_KEYS
Statement.RETURN_GENERATED_KEYS:将生成的主键作为ResultSet返回到PrepareStatement中,因为可能一次插入多条数据,所以结果存储在ResultSet。
Statement.NO_GENERATED_KEYS:则不返回生成的主键
PreparedStatement ps = con.prepareStatement(String sql,int autoGeneratedKeys);
ResultSet rs = ps.getGeneratedKeys();
使用Statement也可以指定参数来获得自动生成的主键,只不过是在execute(String sql,int autoGeneratedKeys)或executeUpdate(String sql,int autoGeneratedKeys)时指定是否返回主键。
九、DAO设计模式
Data Access Object,数据存取对象,是一种用来操作数据库的设计模式,就是将数据库操作都封装起来,对外提供相应的接口
一个典型的DAO模式应该包括以下几个部分:
VO(value object):只包含值(属性),就是对数据库中某个表的映射
DAO接口:只包含方法,就是对于VO中的属性进行的各种操作(增删改查等)。还可以进一步分为BaseDao类 和一般Dao接口,BaseDao类中定义一些基本的数据库连接、资源关闭、SQL语句的执行等基本操作,这样DAO实现类中只需要继承BaseDao类,并实现一般Dao接口就可以。
DAO实现类:方法的具体实现
好处:
1、隔离了数据访问代码和业务逻辑代码。业务逻辑代码直接调用DAO方法即可,完全感觉不到数据库表的存在。分工明确,数据访问层代码变化不影响业务逻辑代码,这符合单一职能原则,降低了藕合性,提高了可复用性。
2、隔离了不同数据库实现。采用面向接口编程,如果底层数据库变化,如由 MySQL 变成 Oracle 只要增加 DAO 接口的新实现类即可,原有 MySQL 实现不用修改,提高了代码扩展性和系统的可移植性。
实例:数据库中有一个存储图书信息的表,表结构和表数据如图所示,分别为图书编号,书名和价格。对其进行DAO封装
项目目录结构:其中vo包中的Book类对应数据库中的book信息表。book表中有几个字段,Book类中就有几个属性
BaseDao中定义了数据库连接、资源关闭、执行SQL语句(采用可变参数将SQL语句的参数传入)
BookDao接口中定义了需要对book表进行的操作,包括插入图书、删除图书、修改图书,查询比较麻烦,分为很多不同的种类,这里只写了最简单的查询情况,即查询所有图书、按编号查询图书和分页查询图书 。
BookDaoImpl作为DAO实现类,需要实现BookDao接口中定义的方法,完成具体的操作语句,这样在使用时,直接调用并将图书信息作为参数传入即可。对于调用者来说,并不需要使用到JDBC。
TestDAO用于测试,模拟使用者直接调用BookDaoImpl中的方法即可。
代码:
Book.java
package Test.com.oracle.vo;
public class Book {
private Integer isbn;
private String bookname;
private Integer price;
public Integer getIsbn() {
return isbn;
}
public void setIsbn(Integer isbn) {
this.isbn = isbn;
}
public String getBookname() {
return bookname;
}
public void setBookname(String bookname) {
this.bookname = bookname;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public Book() {
super();
}
public Book(String bookname, Integer price) {
this.bookname = bookname;
this.price = price;
}
public Book(Integer isbn, String bookname, Integer price) {
this.isbn = isbn;
this.bookname = bookname;
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"isbn=" + isbn +
", bookname='" + bookname + '\'' +
", price=" + price +
'}';
}
}
BaseDao.java
package Test.com.oracle.dao;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BaseDao {
/**
* 获得数据库连接
* @return
*/
public Connection getConnection() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String url = "jdbc:mysql://localhost:3306/oracle?characterEncoding=utf-8";
String user = "root";
String pwd = "root";
Connection con = null;
try {
con = DriverManager.getConnection(url, user, pwd);
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
/**
* 关闭资源,可以关闭一切实现了AutoCloseable接口的
* 例如Connection,ResultSet,PreparedStatement等
*
* @param con
*/
public void close(AutoCloseable con) {
if (con != null) {
try {
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 动态执行SQL语句(主要是增删改),使用可变参数
* 可变参数使用时的方法与数组相同
* 注意:一个方法至多只能有一个可变参数,且需要作为最后一个参数
*
* @param sql
* @param objs
*/
public void executeSql(String sql, Object... objs) {
Connection con = this.getConnection();
PreparedStatement ps = null;
try {
ps = con.prepareStatement(sql);
//设置执行的参数
for (int i = 0; i < objs.length; i++) {
ps.setObject(i + 1, objs[i]);
}
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
this.close(ps);
this.close(con);
}
}
/**
* 动态执行SQL查询语句,并将返回结果封装到List中
* 每条记录对应List中的一个Map,
*
* @param sql
* @param objs
* @return
*/
public List<Map<String, Object>> executeQuery(String sql, Object... objs) {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
Connection conn = this.getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
for (int i = 0; i < objs.length; i++) {
ps.setObject(i + 1, objs[i]);
}
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
while (rs.next()) {
Map<String, Object> map = new HashMap<String, Object>();
for (int i = 1; i <= rsmd.getColumnCount(); i++) {
map.put(rsmd.getColumnLabel(i), rs.getObject(i));
}
list.add(map);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
this.close(rs);
this.close(ps);
this.close(conn);
}
return list;
}
}
BookDao.java
package Test.com.oracle.dao;
import Test.com.oracle.vo.Book;
import java.util.List;
public interface BookDao {
public void insert(Book book);
public void update(Book book);
public void delete(int isbn);
public void delete(int[] isbn);
public List<Book> getAll();
public Book getBookById(int isbn);
public List<Book> getAllByPage(int pageNumber,int pageSize);
}
BookDaoImpl.java
package Test.com.oracle.dao;
import Test.com.oracle.vo.Book;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class BookDaoImpl extends BaseDao implements BookDao {
@Override
public void insert(Book book) {
executeSql("insert into book values(null,?,?)",book.getBookname(),book.getPrice());
}
@Override
public void update(Book book) {
executeSql("update book set bookname=?,price=? where isbn=?",book.getBookname(),book.getPrice(),book.getIsbn());
}
@Override
public void delete(int isbn) {
executeSql("delete from book where isbn=?",isbn);
}
//批量删除
public void delete(int[] isbn) {
}
@Override
public List<Book> getAll() {
List<Map<String,Object>> list = executeQuery("select * from book");
return convert(list);
}
@Override
public Book getBookById(int isbn) {
List<Map<String,Object>> list = executeQuery("select * from book where isbn=?", isbn);
return list.isEmpty()?null:convert(list).get(0);
}
/**
* pageSize为一页显示几本书
* pageNumber为当前显示的是第几页
* @param pageNumber
* @param pageSize
* @return
*/
@Override
public List<Book> getAllByPage(int pageNumber, int pageSize) {
List<Map<String,Object>> list = executeQuery("select * from book order by isbn limit ?,?",
(pageNumber-1)*pageSize,
pageSize);
return convert(list);
}
public List<Book> convert(List<Map<String,Object>> list){
List<Book> bList = new ArrayList<Book>();
for(Map<String,Object> map:list){
Book b = new Book();
b.setIsbn((int)map.get("isbn"));
b.setBookname((String)map.get("bookname"));
b.setPrice((int)map.get("price"));
bList.add(b);
}
return bList;
}
}
TestDao.java
package Test.com.oracle;
import Test.com.oracle.dao.BookDao;
import Test.com.oracle.dao.BookDaoImpl;
import Test.com.oracle.vo.Book;
import java.util.List;
public class TestDAO {
public static void main(String[] args) {
Book b = new Book("活着就是为了改变世界",357);
b.setIsbn(2);
BookDao dao = new BookDaoImpl();
dao.insert(b);
dao.delete(2);
dao.delete(8);
// BookDao dao = new BookDaoImpl();
// List<Book> book = dao.getAll();
// for(Book b:book){
// System.out.println(b);
// }
//
//
// BookDao dao = new BookDaoImpl();
// Book res = dao.getBookById(101);
// if(res!=null){
// System.out.println(res);
// }else{
// System.out.println("不存在这本书");
// }
//
//
// BookDao dao = new BookDaoImpl();
//
// List<Book> book = dao.getAllByPage(2,10);
// for(Book b:book){
// System.out.println(b);
// }
}
}