JDBC 学习笔记(一)—— 基础知识 + 分页技术
本文查阅方法:
1、查阅目录 —— 查阅本文目录,确定想要查阅的目录标题
2、快捷“查找” —— 在当前浏览器页面,按键 “Ctrl+F” 按键组合,开启浏览器的查找功能,
在查找搜索框中 输入需要查阅的 目录标题,便可以直接到达 标题内容 的位置。
3、学习小结 —— 文中的学习小结内容,是笔者在学习之后总结出的,开发时可直接参考其进行应用开发的内容, 进一步加快了本文的查阅 速度。(水平有限,仅供参考。)
本文目录
学习小结
1、JDBC简介
2、使用JDBC的步骤——第一个JDBC程序
3、DriverManager ——加载数据库驱动
4、数据库URL ——标识数据库的位置
5、Connection ——代表数据库的链接
6、Statement ——向数据库发送SQL语句
7、ResultSet ——代表Sql语句的执行结果
8、释放资源 ——释放与数据库进行交互的对象
9、用JDBC对数据库进行CRUD
10、CRUD操作-create 示例Demo
11、CRUD操作-updata 示例Demo
12、CRUD操作-delete 示例Demo
13、CRUD操作-read 示例Demo
14、SQL 注入的防范
15、PreparedStatement
16、数据库分页——实现语句
17、数据库分页—— 采用Dao工厂模式实现数据库分页原理图解
18、数据库分页——执行流程中所需的类及代码
19、数据库分页——QueryInfo类、PageBean类、QueryResult类 三个辅助类的内容
相关学习
JDBC 学习笔记(一)—— 基础知识 + 分页技术
链接地址:http://even2012.iteye.com/blog/1886946
JDBC 学习笔记(二)—— 大数据+存储过程+批处理+事务
链接地址:http://even2012.iteye.com/blog/1886950
JDBC 学习笔记(三)—— 数据源(数据库连接池):DBCP数据源、C3P0 数据源以及自定义数据源技术
链接地址:http://even2012.iteye.com/blog/1886953
JDBC 学习笔记(四)—— 自定义JDBC框架+Apache—DBUtils框架+事务管理+操作多表
链接地址:http://even2012.iteye.com/blog/1886956
学习小结
1、JDBC简介
SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC。
JDBC全称为:Java Data Base Connectivity(java数据库连接),它主要由接口组成。
组成JDBC的2个包:
java.sql
javax.sql
开发JDBC应用需要以上2个包的支持外,还需要导入相应JDBC的数据库实现(即数据库驱动)。
2、使用JDBC的步骤——第一个JDBC程序
需求:编程从user表中读取数据,并打印在命令行窗口中。
(1) 搭建实验环境 :
a、在mysql中创建一个库,并创建user表t同时插入数据到表中。
b、新建一个Java工程,并导入数据库驱动。
(2) 编写程序,在程序中加载数据库驱动
a、方式一:DriverManager. registerDriver(Driver driver)
b、方式二:Class.forName(“com.mysql.jdbc.Driver”);
(3) 建立连接(Connection)
Connection conn = DriverManager.getConnection(url,user,pass);
(4) 创建用于向数据库发送SQL的Statement对象,并发送sql
Statement st = conn.createStatement();
ResultSet rs = st.excuteQuery(sql);
(5) 从代表结果集的ResultSet中取出数据,打印到命令行窗口
(6) 断开与数据库的连接,并释放相关资源
Demo样例:
public static void main(String[] args) throws SQLException {
String url = "jdbc:mysql://localhost:3306/day14";
String username = "root";
String password = "root";
//1.加载驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2。获取链接
Connection conn = DriverManager.getConnection(url, username, password);
//3.获取向数据库发sql语句的statament对象
Statement st = conn.createStatement();
//4.向数据库发送sql,获取数据库返回的结果集
ResultSet rs = st.executeQuery("select * from users");
//5.从结果集中获取数据
while(rs.next()){
System.out.println("id=" + rs.getObject("id"));
System.out.println("name=" + rs.getObject("name"));
System.out.println("password=" + rs.getObject("password"));
System.out.println("email=" + rs.getObject("email"));
System.out.println("birthday=" + rs.getObject("birthday"));
}
//6.释放资源(释放链接)
rs.close();
st.close();
conn.close();
}
3、DriverManager ——加载数据库驱动
Jdbc程序中的DriverManager用于加载驱动,并创建与数据库的链接,这个API的常用方法:
DriverManager.registerDriver(new Driver())
DriverManager.getConnection(url, user, password),
注意:在实际开发中并不推荐采用registerDriver方法注册驱动。原因有二:
一、查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象。
二、程序依赖mysql的api,脱离mysql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。
推荐方式:Class.forName(“com.mysql.jdbc.Driver”);
采用此种方式不会导致驱动对象在内存中重复出现,并且采用此种方式,程序仅仅只需要一个字符串,不需要依赖具体的驱动,使程序的灵活性更高。
同样,在开发中也不建议采用具体的驱动类型指向getConnection方法返回的connection对象。
Demo:数据库JDBC开发使用的模版(Statement对象。):
String url = "jdbc:mysql:///day14";
String username = "root";
String password = "root";
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url, username, password);
st = conn.createStatement(); //throw
CRUD操作……
}finally{
if(rs!=null){
try{
rs.close(); //throw new
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
st = null;
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
4、数据库URL ——标识数据库的位置
URL用于标识数据库的位置,程序员通过URL地址告诉JDBC程序连接哪个数据库,
MySql 数据库的URL写法为: jdbc:mysql:[]//localhost:3306/test ?参数名:参数值
URL图解
常用数据库URL地址的写法:
Oracle写法:jdbc:oracle:thin:@localhost:1521:sid
SqlServer—jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid
MySql—jdbc:mysql://localhost:3306/sid
Mysql的url地址的简写形式: jdbc:mysql:///sid
常用属性:useUnicode=true&characterEncoding=UTF-8
5、Connection ——代表数据库的链接
Jdbc程序中的Connection,它用于代表数据库的链接。Connection 是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过Connection 对象完成的,这个对象的常用方法:
(1) createStatement():创建向数据库发送sql的statement对象。
(2) prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
(3) prepareCall(sql):创建执行存储过程的callableStatement对象。
(4) setAutoCommit(boolean autoCommit):设置事务是否自动提交。
(5) commit() :在链接上提交事务。
(6) rollback() :在此链接上回滚事务。
6、Statement ——向数据库发送SQL语句
Jdbc程序中的Statement对象用于向数据库发送SQL语句, Statement对象常用方法:
(1) executeQuery(String sql) :用于向数据库发送查询语句。
(2) executeUpdate(String sql):用于向数据库发送insert、update或delete语句
(3) execute(String sql):用于向数据库发送任意sql语句
(4) addBatch(String sql) :把多条sql语句放到一个批处理中。
(5) executeBatch():向数据库发送一批sql语句执行。
(6) clearBatch() :清空此 Statement 对象的当前 SQL 命令列表。
7、ResultSet ——代表Sql语句的执行结果
Jdbc程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式。ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。
(1) ResultSet提供了对结果集进行滚动的方法:
a、next():移动到下一行
b、Previous():移动到前一行
c、absolute(int row):移动到指定行
d、beforeFirst():移动resultSet的最前面。
e、 afterLast() :移动到resultSet的最后面。
(2) ResultSet既然用于封装执行结果的,所以该对象提供了用于获取数据的get方法:
获取任意类型的数据
getObject(int index)
getObject(string columnName)
获取指定类型的数据,例如:
getString(int index)
getString(String columnName)
其他获取指定类型数据的方法见下表:
常用数据类型转换表
8、释放资源 ——释放与数据库进行交互的对象
Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。
特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。
9、用JDBC对数据库进行CRUD
Jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。
Statement对象的executeUpdate方法,用于向数据库发送增、删、改的sql语句,executeUpdate执行完后,将会返回一个int整数(即增删改语句导致了数据库几行数据发生了变化)。
Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象。
10、CRUD操作-create 示例Demo
使用executeUpdate(String sql)方法完成数据添加操作,示例操作:
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url, username, password);
Statement st = conn.createStatement();
String sql = "insert into user(….) values(…..) ";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("插入成功!!!");
}
11、CRUD操作-updata 示例Demo
使用executeUpdate(String sql)方法完成数据修改操作,示例操作:
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url, username, password);
Statement st = conn.createStatement();
String sql = “update user set name=‘ ’ where name=‘ ’ ";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println(“修改成功!!!");
}
12、CRUD操作-delete 示例Demo
使用executeUpdate(String sql)方法完成数据删除操作,示例操作:
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url, username, password);
Statement st = conn.createStatement();
String sql = "delete from user where id=1";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println(“删除成功!!!");
}
13、CRUD操作-read 示例Demo
使用executeQuery(String sql)方法完成数据查询操作,示例操作:
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url, username, password);
Statement st = conn.createStatement();
String sql = “select * from user where id=1 “;
ResultSet rs = st.executeUpdate(sql);
while(rs.next()){
//根据获取列的数据类型,分别调用rs的相应方法
//映射到java对象中
}
14、SQL 注入的防范
SQL 注入是用户利用某些系统没有对输入数据进行充分的检查,从而进行恶意破坏的行为。
1、statement存在sql注入攻击问题,例如登陆用户名采用' or 1=1 or username=‘
2、对于防范 SQL 注入,可以采用PreparedStatement取代Statement。
备注:本例只是最基本的防止SQL注入方式,其他情况还请查阅资料。
15、PreparedStatement
PreperedStatement是Statement的孩子,它的实例对象可以通过调用Connection.preparedStatement()方法获得,相对于Statement对象而言的优势:
(1) 防止SQL注入:PreperedStatement可以避免SQL注入的问题。
(2) 预编译SQL语句:Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。PreparedStatement 可对SQL进行预编译,从而提高数据库的执行效率。
(3) 使用占位符简化语句:并且PreperedStatement对于sql中的参数,允许使用占位符的形式进行替换,简化sql语句的编写。 (例如多次循环插入数据)
Demo样例:
public List<Customer> getAll(){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select * from customer";
st = conn.prepareStatement(sql);
rs = st.executeQuery();
List list = new ArrayList();
while(rs.next()){
Customer c = new Customer();
c.setBirthday(rs.getDate("birthday"));
c.setCellphone(rs.getString("cellphone"));
c.setDescription(rs.getString("description"));
c.setEmail(rs.getString("email"));
c.setGender(rs.getString("gender"));
c.setId(rs.getString("id"));
c.setName(rs.getString("name"));
c.setPreference(rs.getString("preference"));
c.setType(rs.getString("type"));
list.add(c);
}
return list;
}catch (Exception e) {
throw new DaoException(e);
}finally{
JdbcUtils.release(conn, st, rs);
}
}
16、数据库分页——实现语句
(1) MySQL分页
MySQL分页的实现语句:
Select * from table limit M,N
M:记录开始索引位置
N:取多少条记录。
完成WEB页面的分页显示的步骤:
(a) 先获得需分页显示的记录总数,然后在web页面中显示页码。
(b) 根据页码,从数据库中查询相应的记录显示在web页面中。
以上两项操作通常使用Page对象进行封装。
(2) Oracle分页
Oracle分页的实现语句:
select * from (
select rownum r_, row_.* from (
select * from student order by id
) row_ where rownum <=5
) where r_>=1
1位置:起始索引位置。
5位置:结束索引位置。
备注:关于Oracle的分页,此处仅是简单讲解,会在Oracle 专题博文中详述。
17、数据库分页—— 采用Dao工厂模式实现数据库分页原理图解
(1) dao工厂模式的原理示意图
(2) 数据库分页 原理图解
18、数据库分页——执行流程中所需的类及代码
(1)Servlet层:ListCustomerServlet .java
//处理用户分页请求
public class ListCustomerServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try{
QueryInfo info = WebUtils.request2Bean(request, QueryInfo.class);
BusinessService service = new BusinessServiceImpl();
PageBean pagebean = service.pageQuery(info);
request.setAttribute("pagebean", pagebean);
request.getRequestDispatcher("/WEB-INF/jsp/listcustomer.jsp").forward(request, response);
}catch (Exception e) {
e.printStackTrace();
request.setAttribute("message", "查看客户失败!!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
(2) Service层:BusinessServiceImpl.java
public class BusinessServiceImpl implements BusinessService {
public PageBean pageQuery(QueryInfo queryInfo){
//调用dao获取到页面数据
QueryResult qr = dao.pageQuery(queryInfo.getStartindex(), queryInfo.getPagesize());
//根据dao查询结果,生成页面显示需要pagebean
PageBean bean = new PageBean();
bean.setCurrentpage(queryInfo.getCurrentpage());
bean.setList(qr.getList());
bean.setPagesize(queryInfo.getPagesize());
bean.setTotalrecord(qr.getTotalrecord());
return bean;
}
}
(3)Dao层:CustomerDaoImpl.java
public class CustomerDaoImpl implements CustomerDao {
//获取到页面数据和页面大小
public QueryResult pageQuery(int startindex,int pagesize){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
QueryResult qr = new QueryResult();
try{
conn = JdbcUtils.getConnection();
String sql = "select * from customer limit ?,?";
st = conn.prepareStatement(sql);
st.setInt(1, startindex);
st.setInt(2, pagesize);
rs = st.executeQuery();
List list = new ArrayList();
while(rs.next()){
Customer c = new Customer();
c.setBirthday(rs.getDate("birthday"));
c.setCellphone(rs.getString("cellphone"));
c.setDescription(rs.getString("description"));
c.setEmail(rs.getString("email"));
c.setGender(rs.getString("gender"));
c.setId(rs.getString("id"));
c.setName(rs.getString("name"));
c.setPreference(rs.getString("preference"));
c.setType(rs.getString("type"));
list.add(c);
}
qr.setList(list);
sql = "select count(*) from customer";
st = conn.prepareStatement(sql);
rs = st.executeQuery();
if(rs.next()){
qr.setTotalrecord(rs.getInt(1));
}
return qr;
}catch (Exception e) {
throw new DaoException(e);
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
19、数据库分页——QueryInfo类、PageBean类、QueryResult类 三个辅助类的内容
(1)QueryInfo.java 类中的内容:
public class QueryInfo {
private int currentpage = 1; //用户当前看的页
private int pagesize = 5; //记住用户想看的页面大小
private int startindex; //记住用户看的页的数据在数据库的起始位置
public int getCurrentpage() {
return currentpage;
}
public void setCurrentpage(int currentpage) {
this.currentpage = currentpage;
}
public int getPagesize() {
return pagesize;
}
public void setPagesize(int pagesize) {
this.pagesize = pagesize;
}
public int getStartindex() {
this.startindex = (this.currentpage-1)*this.pagesize;
return startindex;
}
}
(2)PageBean.java 类中的内容:
public class PageBean {
private List list;
private int totalrecord;
private int pagesize;
private int totalpage;
private int currentpage;
private int previouspage;
private int nextpage;
private int[] pagebar;
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public int getTotalrecord() {
return totalrecord;
}
public void setTotalrecord(int totalrecord) {
this.totalrecord = totalrecord;
}
public int getPagesize() {
return pagesize;
}
public void setPagesize(int pagesize) {
this.pagesize = pagesize;
}
public int getTotalpage() {
//100 5 20
//101 5 21
//99 5 20
if(this.totalrecord%this.pagesize==0){
this.totalpage = this.totalrecord/this.pagesize;
}else{
this.totalpage = this.totalrecord/this.pagesize+1;
}
return totalpage;
}
public int getCurrentpage() {
return currentpage;
}
public void setCurrentpage(int currentpage) {
this.currentpage = currentpage;
}
public int getPreviouspage() {
if(this.currentpage-1<1){
this.previouspage = 1;
}else{
this.previouspage = this.currentpage-1;
}
return previouspage;
}
public int getNextpage() {
if(this.currentpage+1>=this.totalpage){
this.nextpage = this.totalpage;
}else{
this.nextpage = this.currentpage +1;
}
return nextpage;
}
public int[] getPagebar() {
int startpage;
int endpage;
int pagebar[] = null;
if(this.totalpage<=10){
pagebar = new int[this.totalpage];
startpage = 1;
endpage = this.totalpage;
}else{
pagebar = new int[10];
startpage = this.currentpage - 4;
endpage = this.currentpage + 5;
//总页数=30 3 -1
//总页数=30 29 34 21 30
if(startpage<1){
startpage = 1;
endpage = 10;
}
if(endpage>this.totalpage){
endpage = this.totalpage;
startpage = this.totalpage - 9;
}
}
int index = 0;
for(int i=startpage;i<=endpage;i++){
pagebar[index++] = i;
}
this.pagebar = pagebar;
return this.pagebar;
/*int pagebar[] = new int[this.totalpage];
for(int i=1;i<=this.totalpage;i++){
pagebar[i-1] = i;
}
this.pagebar = pagebar;
return pagebar;*/
}
}
(3)QueryResult.java 类中的内容:
public class QueryResult {
private List list; //记住用户看的页的数据
private int totalrecord; //记往总记录数
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public int getTotalrecord() {
return totalrecord;
}
public void setTotalrecord(int totalrecord) {
this.totalrecord = totalrecord;
}
}
敬请评论
(1)若您觉得本文 有用处 —— 请留言评论,以坚定其他 IT童鞋 阅读本文的信心。
(2)若您觉得本文 没用处 —— 请留言评论,笔者将会改进不足,以便为大家整理更加好用的笔记。