一、Tomcat简介
1、Tomcat简介
Tomcat 是 Apache 软件基金会(Apache Software Foundation)的 Jakarta 项目中的一个核心项目,由 Apache、Sun 和其他一些公司及个人共同开发而成。由于有了 Sun 的参与和支持,最新的 Servlet 和JSP 规范总是能在 Tomcat 中得到体现,Tomcat 5 支持最新的 Servlet 2.4 和 JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受 Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的 Web 应用服务器。
Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试 JSP 程序的首选。 Tomcat 和 IIS 等 Web 服务器一样,具有处理 HTML 页面的功能。不过,Tomcat 处理静态 HTML 的能力不如 Apache 服务器。
官方网站:Apache Tomcat® - Welcome!
2、JSP简介
JSP:全名为Java Server Pages,中文名叫java服务器页面, 是由Sun Microsystems公司倡导、许多公司参与一起建立的一种动态网页技术标准。JSP技术有点类似ASP技术,它是在传统的网页HTML(标准通用标记语言的子集)文件(*.htm,*.html)中插入Java程序段(Scriptlet)和JSP标记(tag),从而形成JSP文件,后缀名为(*.jsp)。 用JSP开发的Web应用是跨平台的,既能在Linux下运行,也能在其他操作系统上运行。
支持 JSP 网站,收费版 web 服务器:oracle 的 weblogic ; IBM 的 websphere
WebLogic 是美国 Oracle 公司出品的一个 application server 确切的说是一个基于 JAVAEE 架构的中间件,WebLogic 是用于开发、集成、部署和管理大型分布式 Web 应用、网络应用和数据库应用的 Java应用服务器。将 Java 的动态功能和 Java Enterprise 标准的安全性引入大型网络应用的开发、集成、部署和管理之中。
WebSphere 是 IBM 的软件平台。它是 Web 应用程序和跨平台、跨产品解决方案所需要的整个中间件基础设施。WebSphere 提供了可靠、灵活和健壮的软件运行服务。
3、中间件
中间件是一种独立的系统软件或服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源。中间件位于客户机/服务器的操作系统之上,管理计算机资源和网络通讯。是连接两个独立应用程序或独立系统的软件。相连接的系统,即使它们具有不同的接口,但通过中间件相互之间仍能交换信息。执行中间件的一个关键途径是信息传递。通过中间件,应用程序可以工作于多平台或OS环境。
中间件是一类连接软件组件和应用的计算机软件,它包括一组服务。以便于运行在一台或多台机器上的多个软件通过网络进行交互。该技术所提供的互操作性,推动了一致分布式体系架构的演进,该架构通常用于支持并简化那些复杂的分布式应用程序,它包括web服务器、事务监控器和消息队列软件。
二、Java简介
1、JDK简介
JDK : java development kit (套件) 。简单的说 JDK 是面向开发人员使用的 SDK,它提供了 Java的开发环境和运行环境。就是Java开发工具,是进行Java开发的基础。
JDK(Java Development Kit)是Sun Microsystems针对Java开发人员的产品。自从Java推出以来,JDK已经成为使用最广泛的Java SDK。JDK 是整个Java的核心,包括了Java运行环境,Java工具和Java基础的类库。JDK是学好Java的第一步。而专门运行在x86平台的Jrocket在服务端运行效率也要比Sun JDK好很多。从SUN的JDK5.0开始,提供了泛型等非常实用的功能,其版本也不断更新,运行效率得到了非常大的提高。
SDK:Software Development Kit,软件开发工具包,一般都是一些软件工程师为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件时的开发工具的集合。可以包括函数库、编译程序等。
JRE:java Runtime Enviroment 是指 Java 的运行环境,是面向 Java 程序的使用者,而不是开发者,运行JAVA程序所必须的环境的集合,包含JVM标准实现及Java核心类库。Java Runtime Environment(包括Java Plug-in)是Sun的产品,包括两部分:Java Runtime Environment和Java Plug-in。JRE是可以在其上运行、测试和传输应用程序的Java平台。它包括Java虚拟机(jvm)、Java核心类库和支持文件。它不包含开发工具(JDK)--编译器、调试器和其它工具。JRE需要辅助软件--Java Plug-in--以便在浏览器中运行applet。
2、JVM简介
JVM:java virtual machineJVM 就是我们常说的 java 虚拟机。JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM是java的核心和基础,在java编译器和OS平台之间的虚拟处理器。
它是一种基于下层的操作系统和硬件平台并利用软件方法来实现的抽象的计算机,可以在上面执行java的字节码程序。java编译器只需面向JVM,生成JVM能理解的代码或字节码文件。Java源文件经编译器,编译成字节码程序,通过JVM将每一条指令翻译成不同平台机器码,通过特定平台运行。
在 JDK的安装目录里你可以找到 jre目录里面有两个文件夹bin 和 lib,在这里可以认为 bin 里的就是 jvm, lib 中则是 jvm 工作所需要的类库,而 jvm 和 lib 和起来就称为 jre。
三、Tomcat工作模式
模式:B/S模式;
端口:8080;
Java SE:是由Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java平台的总称。用Java实现的HotJava浏览器(支持Java applet)显示了Java的魅力:跨平台、动态的Web、Internet计算。从此,Java被广泛接受并推动了Web的迅速发展,常用的浏览器现在均支持Java applet。
Applet:小应用程序是采用Java编程语言编写的程序,该程序可以包含在 HTML(标准通用标记语言的一个应用)页中,与在页中包含图像的方式大致相同。
SE(J2SE),standard edition,标准版,是我们通常用的一个版本,从JDK 5.0开始,改名为Java SE。
EE(J2EE),enterprise edition,企业版,使用这种JDK开发J2EE应用程序,从JDK 5.0开始,改名为Java EE。
ME(J2ME),micro edition,主要用于移动设备、嵌入式设备上的java应用程序,从JDK5.0开始,改名为Java ME。
没有JDK的话,无法编译Java程序,如果想只运行Java程序,要确保已安装相应的JRE。
四、Tomcat及相关软件
1、Tomcat下载
我们选择的是通用的二进制安装包,64位的
Tomcat下载地址:Apache Tomcat® - Welcome!
页面选择你需要的版本:
点击版本号跳转到相应的软件包类型选择下载(我们这里选择tar包):
2、MySQL-Connector-Java下载
官网下载网站:MySQL :: MySQL Community Downloads
选择Connector/j 跳转下载页面:
选择Paltform Independent,选择tar包后面的Download:
不用注册,No thanks,just start my download:
3、Tomcat-native
Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。直接说就是用tomcat-native这个软件来提高tomcat处理静态页面的性能。这个软件在tomcat的bin目录下已经提供,不用单独去下载了!可以tomcat处理静态的性能略逊于apache!
4、MySQL下载
mysql官方下载网址:MySQL :: Download MySQL Community Server
选择版本下载即可。
五、部署Tomcat使用jsp连接MySQL
所有软件的版本如下:
MySQL-Connector-Java: mysql-connector-java-5.1.36
Tomcat: apache-tomcat-8.5.24
JDK: jdk-8u152-linux-x64.tar.gz
MySQL: mysql-5.6.26
常见的网页类型:
HTML&HTM:HyperText Markup Language 超文本连接标示语言 .html .html
ASP:Active Server Page 动态服务器页面(微软开发) .asp
ASP.net:ASP的下一个版本,也是建立在通用语言上的程序架构,网页后缀如aspx
PHP:Hypertext Preprocessor 超级本本预处理语言 .php .php5 .phps
JSP:JAVA Server Pages Sun Microsystems公司倡导,有点类似ASP技术 .jsp
1、安装JDK8
注意:安装之前需要查看下系统是否安装了openjdk,如果安装了openjdk,请先卸载,否则安装不了oracle官方的jdk。
卸载方法:
yum remove java-* -y
注意:生成环境中建议大家统一使用oracle-jdk,他比openjdk的性能好,兼容性大。
解压文件到目录下:
[root@localhost opt]# tar xf jdk-8u152-linux-x64.tar.gz -C /usr/local/
[root@localhost opt]# cd /usr/local/
[root@localhost local]# ln -sv jdk1.8.0_152 jdk1.8
‘jdk1.8’ -> ‘jdk1.8.0_152’
配置环境变量,让系统变量读到jdk的路径:
[root@localhost local]# vim /etc/profile.d/jdk8.sh
export JAVA_HOME=/usr/local/jdk1.8.0_152
export CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/jar/tools.jar:$JAVA_HOME/jre/lib
export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
重读变量,生效:
[root@localhost local]# . /etc/profile.d/jdk8.sh
[root@localhost local]# java -version
java version "1.8.0_152"
Java(TM) SE Runtime Environment (build 1.8.0_152-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode)
2、安装tomcat
[root@XueGod63 ~]# tar xvf apache-tomcat-8.5.24.tar.gz -C /usr/local/src/
[root@XueGod63 ~]# cd /usr/local/src/apache-tomcat-8.5.24/
注意:Tomcat有两种安装方式,一种是直接解压就可以使用的二进制文件,第二种是编译安装,我们这里采用了第一种方法,下载的tomcat软件直接解压就可以执行的二进制文件,因为在官方默认下载的便是使用第一种方法安装,那么下载解压后的目录如下:
tomcat|---bin Tomcat:存放启动和关闭tomcat脚本;|---conf Tomcat:存放不同的配置文件(server.xml和web.xml);|---lib:包含Tomcat使用的jar文件.unix平台此目录下的任何文件都被加到Tomcat的classpath中;|---logs:存放Tomcat执行时的LOG文件;
|---webapps:Tomcat的主要Web发布目录(包括应用程序示例);|--- ROOT:tomcat的家目录|--- index.jsp:Tomcat的默认首页文件
|---work:存放jsp编译后产生的class文件或servlet文件存放
|---temp:存放Tomcat运行时所产生的临时文件
[root@XueGod63 apache-tomcat-8.5.24]# ls bin/ #tomcat的执行脚本文件
bootstrap.jar configtest.bat setclasspath.sh tomcat-native.tar.gz
catalina.bat configtest.sh shutdown.bat tool-wrapper.bat
catalina.sh daemon.sh shutdown.sh tool-wrapper.sh
catalina-tasks.xml digest.bat startup.bat version.bat
commons-daemon.jar digest.sh startup.sh version.sh
commons-daemon-native.tar.gz setclasspath.bat tomcat-juli.jar
#bat为windows下的脚本(批处理)
#sh为Linux下的脚本,Shell
#catalina.sh是一个最关键的脚本,其他的启动脚本如startup,shutdown.sh,都是使用了不同的参数调用了该脚本,startup是以start参数调用了catalina脚本,shutdown使用stop调用了catalina脚本!
[root@XueGod63 apache-tomcat-8.5.24]# cd .. ; mv apache-tomcat-8.5.24/ /usr/local/tomcat ; cd
Tomcat启动脚本:
[root@Xuegod68 ~]# vim /etc/init.d/tomcat
#!/bin/bash
#
# tomcat startup script for the Tomcat server
#
# chkconfig: 345 80 20
# description: start the tomcat deamon
#
# Source function library
JAVA_HOME=/usr/local/jdk1.8.0_152
export JAVA_HOME
CATALANA_HOME=/usr/local/tomcat
export CATALINA_HOME
case "$1" in
start) echo "Starting Tomcat..." $CATALANA_HOME/bin/startup.sh ;;
stop) echo "Stopping Tomcat..." $CATALANA_HOME/bin/shutdown.sh ;;
restart) echo "Stopping Tomcat..." $CATALANA_HOME/bin/shutdown.sh sleep 2 echo echo "Starting Tomcat..." $CATALANA_HOME/bin/startup.sh ;;
*) echo "Usage: $prog {start|stop|restart}" ;;
esac
exit 0
启动Tomcat:
[root@XueGod63 ~]# chmod +x /etc/init.d/tomcat
[root@XueGod63 ~]# service tomcat start
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR:/usr/local/tomcat/temp
Using JRE_HOME: /usr
Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Tomcat started
[root@XueGod63 ~]# chkconfig --add tomcat
[root@XueGod63 ~]# chkconfig tomcat on
[root@XueGod63 ~]# netstat -antup | grep 8080 #查看是否启动
tcp 0 0:::8080 :::* LISTEN 3154/java
测试Tomcat:
览器访问 http:#192.168.1.63:8080。
3、tomcat管理配置
点击 manager App,需要一个用户名和密码:
创建管理Manger App用户:
[root@Xuegod63 ~]# vim /usr/local/tomcat/conf/tomcat-users.xml
<!--<role rolename="tomcat"/><role rolename="role1"/><user username="tomcat" password="tomcat" roles="tomcat"/><user username="both" password="tomcat" roles="tomcat,role1"/><user username="role1" password="tomcat" roles="role1"/>
-->
#去掉注释<!-- --> 修改为如下, <role rolename="admin-gui"/>
<role rolename="admin-script"/>
<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<role rolename="manager-jmx"/>
<role rolename="manager-status"/>
<user username="admin" password="123456" roles="manager-gui,manager-script,manager-jmx,manager-status,admin-script,admin-gui"/>
角色说明:
1:“manager-gui”:Allows access to the html interface(允许通过web的方式登录查看服务器信息)。
2:“manager-script”: Allows access to the plain text interface(允许以纯文本的方式访问)。
3:“manager-jmx”: Allows access to the JMX proxy interface(允许jmx的代理访问)。
4:“manager-status”: Allows access to the read-only status pages(允许以只读状态访问)。
5: admin-gui: 允许访问HTML GUI
6 : admin-script: 允许访问文本接口
详情参考官网:Apache Tomcat 9 (9.0.65) - Manager App How-To
tomcat8以上还要增加以下配置:
[root@Xuegod63 ~]# vim /usr/local/tomcat/conf/Catalina/localhost/manager.xml
<Context privileged="true" antiResourceLocking="false"docBase="${catalina.home}/webapps/manager"><Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="^.*$" />
</Context>
重启tomcat:
[root@Xuegod63~]# /etc/init.d/tomcat stop && /etc/init.d/tomcat start
4、搭建基于域名的虚拟主机
[root@XueGod63 ~]# ls /usr/local/tomcat/conf/
Catalina catalina.properties logging.properties tomcat-users.xml web.xml
catalina.policy context.xml server.xml tomcat-users.xsd
配置文件说明:
server.xml是Tomcat的主配置文件(全局),服务器设置的,例如端口设置,路径设置。
context里设置tomcat数据源,用来连接数据库。
tomcat_user主要是用户名和密码的设置。
web是默认首页等等之类的设置
[root@XueGod63 ~]# vim /usr/local/tomcat/conf/server.xml
<Host name="xuegod.com" appBase="/var/www/html">
<Context path="" docBase="/var/www/html/web1" />
</Host>
<Host name="xuegod.cn" appBase="/var/www/html">
<Context path="" docBase="/var/www/html/web2" />
</Host>
#在末尾的</Engine>上面添加上面这几行
appBase指定应用程序(网站)的基本路径,这里可以存放多个程序(网站),一般是相对路径,相对于tomcat的安装目录。
Context path=""为虚拟目录,如果是空,表示直接就是/,如果是如path="aa",那么访问的时候就是site:8080/aa
docBase="……" 为实际目录,就是可以是绝对路径,如果是相对路径就是基于appBase
[root@XueGod63 ~]# service tomcat stop ; service tomcat start
[root@XueGod63 ~]# mkdir -p /var/www/html/{web1,web2}
[root@XueGod63 ~]# echo xuegod.com > /var/www/html/web1/index.html
[root@XueGod63 ~]# echo xuegod.cn> /var/www/html/web2/index.html
客户机host文件:
192.168.1.63 xuegod.com
192.168.1.63 xuegod.cn
网页测试:
xuegod.com:8080 xuegod.cn:8080
真实的生产环境中,需要一个合法的域名和一个合法的公网IP,把域名解析到IP上,然后在web服务器里修改配置文件。
5、安装tomcat-Native
Tomcat 可以使用 apr 来提供更好的伸缩性、性能和集成到本地服务器技术。用来提高 tomcat 的性能。 tomcat native 在具体的运行平台上,提供了一种优化技术,它本身是基于 ARP(Apache Portable(轻便) Runtime)技术。
我们应用了 tomcat native 技术之后,tomcat 在跟操作系统级别的交互方面可以做得更好,并且它更像apache 一样,可以更好地作为一台 web server。 tomcat 可以利用 apache 的 apr 接口,使用操作系统的部分本地操作,从而提升性能APR 提升的是静态页面处理能力。
注意:这个包在tomcat8的bin目录已经提供。
关于tomcat-native的相关介绍及下载: Apache Tomcat® - Tomcat Native Downloads
[root@XueGod63 ]# yum install apr-devel gcc gcc-c++ openssl-devel openssl
[root@localhost ~]# cd /usr/local/tomcat/bin/
[root@localhost bin]# cp tomcat-native.tar.gz /usr/local/
[root@localhost bin]# cd /usr/local/
[root@localhost local]# tar xf tomcat-native.tar.gz -C /usr/local/src/
[root@localhost local]# cd /usr/local/src/
[root@localhost src]# cd tomcat-native-1.2.16-src/native/
[root@localhost native]# ./configure --with-apr=/usr/ --with-java-home=/usr/local/jdk1.8.0_152 --with-ssl
[root@localhost native]# make && make install
安装完提示:
需要添加库文件:
[root@xuegod63 native]# vim /etc/ld.so.conf
/usr/local/apr/lib ##添加此行
[root@xuegod63 native]# ldconfig
[root@xuegod63 native]# echo "ldconfig" >>/etc/rc.local
添加环境变量:
[root@localhost native]# vim /etc/profile.d/jdk8.sh
最后添加:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/apr/lib
重读生效:
[root@localhost native]#source /etc/profile.d/jdk8.sh
6、配置Tomcat默认以apr运行模式
tomcat中一共有三种运行模式,分别是:bio、nio、apr。
bio是阻塞式IO操作,使用的是传统的java i/o处理方式,对于每一个请求都要创建一个线程来进行处理,所以开销较大不适合处理高并发的场景。
nio是基于java中非阻塞IO操作的API实现,比传统的i/o处理方式有更高的并发运行性能。
apr是从操作系统级别解决异步IO问题,大幅度提高服务器的并发处理性能,也是Tomcat生产环境运行的首选方式。
编辑主服务文件,指定处理协议为apr:
[root@localhost native]# vim /usr/local/tomcat/conf/server.xml
将默认的protocol="HTTP/1.1"修改为protocol="org.apache.coyote.http11.Http11AprProtocol"
引用apr:
[root@localhost native]# vim /usr/local/tomcat/bin/catalina.sh
253行下面添加:
JAVA_OPTS="$JAVA_OPTS -Djava.library.path=/usr/local/apr/lib"
重启服务生效:
[root@XueGod63 ~]# service tomcat stop ; service tomcat start
[root@xuegod63 ~]# cat /usr/local/tomcat/logs/catalina.out | grep Native #看日志看是否支持native ,是否有看到APR版本号
17-Sep-2015 20:09:24.293 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent Loaded APR based Apache Tomcat Native library 1.1.33 using APR version 1.3.9.
17-Sep-2015 20:09:24.293 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
7、配置https加密访问
[root@xuegod63 ~]# vim /usr/local/tomcat/conf/server.xml #启用SSL证书及端口
强制http跳转到https访问:
[root@xuegod63 ~]# vim /usr/local/tomcat/conf/web.xml #加入方框的代码 重启即可
8、安装mysql
安装MySQL部分,根据实际情况来选择安装方式,可以选择编译安装,也可以用rpm包来安装,只要保证服务正常运行即可。
[root@localhost ~]# yum install mariadb mariadb-server -y
测试数据:
mysql> create database tomcat; #测试数据库,为了和后面方便测试,这里创建tomcat
mysql> use tomcat
mysql> create table tt(id int,name varchar(128)); #创建测试表
mysql> insert into tt values (1,"come on boy"),(2,"come on girl");#创建测试数据
mysql> grant all on tomcat.* to tomcat@localhost identified by 'tomcat'; #授权用户
mysql> \q
[root@XueGod63 ~]# mysql -utomcat -ptomcat #测试tomcat可以登陆(OK)
9、jsp链接mysql
Jsp链接mysql,官方提供了工具mysql-connector。
安装mysql-connector:
[root@XueGod63 ~]# tar xvf mysql-connector-java-5.1.36.tar.gz -C /usr/local/src/[root@XueGod63 ~]# cd /usr/local/src/mysql-connector-java-5.1.36/
[root@XueGod63 ~]# cp /usr/local/src/mysql-connector-java-5.1.36/mysql-connector-java-5.1.36-bin.jar /usr/local/tomcat/lib/ #只需要复制到tomcat的lib目录下,重启tomcat就可以生效
[root@XueGod63 ~]# service tomcat stop; service tomcat start
测试
[root@XueGod63 ~]# vim /usr/local/tomcat/webapps/ROOT/mysql.jsp #建立测试页面
<%@ page contentType="text/html;charset=utf-8"%>
<%@ page import="java.sql.*"%>
<html>
<body>
<%
Class.forName("org.gjt.mm.mysql.Driver").newInstance();
String url ="jdbc:mysql:#localhost/tomcat?user=tomcat&password=tomcat&useUnicode=true&characterEncoding=utf-8";
Connection conn= DriverManager.getConnection(url);
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql="select * from tt";
ResultSet rs=stmt.executeQuery(sql);
while(rs.next()){%>
step:<%=rs.getString(1)%>
context:<%=rs.getString(2)%><br><br>
<%}%>
<%out.print("Congratulations!!! JSP connect MYSQL IS OK!!");%>
<%rs.close();
stmt.close();
conn.close();
%>
</body>
</html>
#代码复制注意核对,由于编码问题,复制到vim中可能会增加多余的%号,记得删除
浏览器访问:http://192.168.1.63:8080/mysql.jsp
六、JDBC API详解
1、JDBC简介
JDBC(Java DataBase Connectivity,Java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系型数据库提供统一访问,它是由一组用 Java 语言编写的类和接口组成的。
JDBC 本质:其实就是 Java 官方提供的一套规范(接口),用于帮助开发人员快速实现不同关系型数据库的连接。
2、JDBC 快速入门
package com.demo;import java.sql.*;public class JdbcDemo {public static void main(String[] args) throws ClassNotFoundException, SQLException {// 1. 导入jar包// 2. 注册驱动// Class.forName("com.mysql.jdbc.Driver"); // Mysql5 驱动Class.forName("com.mysql.cj.jdbc.Driver"); // Mysql8 驱动// 3. 获取连接对象Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/world?serverTimezone=UTC", "root", "admin");// 4. 获取执行对象Statement statement = connection.createStatement();// 5. 执行SQL语句,并接收结果String sql = "select * from city limit 5";ResultSet resultSet = statement.executeQuery(sql);// 6. 处理结果while(resultSet.next()) {System.out.println(resultSet.getString("Name") + "\t" + resultSet.getString("Population"));}// 7. 释放资源statement.close();connection.close();}
}
3、JDBC API 详解
① DriverManager(驱动管理对象)
1)注册驱动(告诉程序该使用哪一个数据库驱动)
- 注册给定的驱动程序:static void registerDriver(Driver driver);
- 写代码时使用:Class.forName("com.mysql.jdbc.Driver");
- 通过查看源码发现:在 com.mysql.jdbc.Driver 类中存在以下静态代码块
static {try {java.sql.DriverManager.registerDriver(new Driver());} catch (SQLException E) {throw new RuntimeException("Can't register driver!");}
}
- 我们不需要通过 DriverManager 调用静态方法 registerDriver,因为只要 Driver 类被使用,则会执行其静态代码块完成注册驱动。
- Mysql5 之后可以省略注册驱动的步骤。因为在驱动 jar 包中,存在一个 javasql.Driver 配置文件,文件中指定了 com.mysqljdbc.Driver。
2)获取数据库连接对象
static Connection getConnection(String url, String user, String password);
- url:指定连接的路径。语法:
jdbc:mysql://ip地址(域名):端口号/库名
- user:数据库用户名
- password:数据库密码
- 返回值 Connection:数据库连接对象
② Connection(数据库连接对象)
- 获取执行对象
- 获取普通执行对象:
Statement createStatement();
- 获取预编译执行对象:
PreparedStatement prepareStatement(String sql);
- 获取普通执行对象:
- 管理事务
- 开启事务:setAutoCommit(boolean autoCommit); // 参数为 false,则开启事务
- 提交事务:commit();
- 回滚事务:rollback();
- 释放资源
- 立即释放连接对象:void close();
③ Statement(SQL 执行对象)
-
执行 DML 语句:
int executeUpdate(String sql);
- 返回值 int:返回影响的行数。
- 参数 sql:可以执行 insert、update、delete 语句
-
执行 DQL 语句:
ResultSet executeQuery(String sql);
- 返回值 ResultSet:封装查询的结果。
- 参数 sql:可以执行select语句。
-
释放资源
- 立即释放执行对象:void close();
④ ResultSet(结果集对象)
- 判断结果集中是否还有数据:
boolean next();
- 有数据则返回 true,并将索引向下移动一行
- 没有数据则返回 false
- 获取结果集中的数据:
XXX getXxx("列名");
- XXX 代表要获取的某列数据的类型
- 例如:
String getString("name");
、int getInt("age");
- 释放资源
- 立即释放结果集对象:void close();
⑤ PreparedStatement 预编译
1)SQL注入
在登录界面,输入一个错误的用户名或密码,也可以登录成功:
SQL 注入的原理:
- 按照正常道理来说,我们在密码处输入的所有内容,都应该作为密码(这个参数)。
- 但是现在 Statement 对象在执行 SQL 语句时,将输入的内容当做查询条件来执行了。
2)PreparedStatement 预编译
PreparedStatement 即预编译 SQL 语句的执行对象,是 SQL 注入的防御手段之一。其原理是:在执行 SQL 语句之前,将 SQL 语句进行提前编译,在明确 SQL 语句的格式(执行计划)后,就不会改变了。因此剩余的内容都会认为是参数。
参数使用?
作为占位符:
- 为参数赋值的方法:
setXxx(参数 1, 参数 2);
- 参数 1:? 的位置编号(编号从 1 开始)
- 参数 2:? 的实际参数
- 执行 SQL 语句的方法
- 执行 insert、update、delete 语句:
int executeUpdate();
- 执行 select 语句:
ResultSet executeQuery();
- 执行 insert、update、delete 语句:
七、JDBC CRUD案例
1、表数据
-- 创建student表
CREATE TABLE student(sid INT PRIMARY KEY AUTO_INCREMENT, -- 学生idNAME VARCHAR(20), -- 学生姓名age INT, -- 学生年龄birthday DATE -- 学生生日
);-- 添加数据
INSERT INTO student VALUES (NULL,'张三',23,'1999-09-23'), (NULL,'李四',24,'1998-08-10'), (NULL,'王五',25,'1996-06-06'), (NULL,'赵六',26,'1994-10-20');
2、JDBC配置信息
config.properties:
driverClass=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/world?serverTimezone=UTC
username=root
password=admin
3、domain 实体类
Student 实体类:
package com.bean;import java.util.Date;public class Student {private Integer sid;private String name;private Integer age;private Date birthday;public Student() {}public Student(Integer sid, String name, Integer age, Date birthday) {this.sid = sid;this.name = name;this.age = age;this.birthday = birthday;}public Integer getSid() {return sid;}public void setSid(Integer sid) {this.sid = sid;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}@Overridepublic String toString() {return "student{" +"sid=" + sid +", name='" + name + '\'' +", age=" + age +", birthday=" + birthday +'}';}
}
4、JDBC工具类
package com.utils;import java.io.InputStream;
import java.sql.*;
import java.util.Properties;// JDBC 工具类
public class JdbcUtil {// 私有构造方法private JdbcUtil(){}// 声明所需要的配置变量private static String driverClass;private static String url;private static String username;private static String password;private static Connection connection;// 静态代码块:读取配置文件的信息为变量赋值,注册驱动static {try{// 读取配置文件InputStream resourceAsStream = JdbcUtil.class.getClassLoader().getResourceAsStream("config.properties");Properties properties = new Properties();properties.load(resourceAsStream);// 赋值driverClass = properties.getProperty("driverClass");url = properties.getProperty("url");username = properties.getProperty("username");password = properties.getProperty("password");// 注册驱动Class.forName(driverClass);} catch (Exception e) {e.printStackTrace();}}// 获取数据库连接对象public static Connection getConnection() {try {connection = DriverManager.getConnection(url, username, password);} catch (SQLException e) {e.printStackTrace();}return connection;}// 释放资源public static void close(Connection connection, Statement statement, ResultSet resultSet){if (connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}if (statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if (resultSet != null) {try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}}// 释放资源public static void close(Connection connection, Statement statement) {if (connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}if (statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}}
}
5、Dao 层
StudentDao.java:
package com.dao;import com.bean.Student;import java.util.ArrayList;// Dao层接口
public interface StudentDao {// 查询所有学生信息public abstract ArrayList<Student> findAll();// 根据id条件查询public abstract Student findById(Integer id);// 新增学生信息public abstract int insert(Student student);// 修改学生信息public abstract int update(Student student);// 根据id删除学生信息public abstract int delete(Integer id);
}
StudentDaoImpl.java:
package com.dao;import com.bean.Student;
import com.utils.JdbcUtil;import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;public class StudentDaoImpl implements StudentDao{// 查询所有学生信息@Overridepublic ArrayList<Student> findAll() {ArrayList<Student> studentList = new ArrayList<>();Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {connection = JdbcUtil.getConnection();String sql = "select * from student";PreparedStatement preparedStatement = connection.prepareStatement(sql);resultSet = preparedStatement.executeQuery();// 处理结果集while (resultSet.next()) {Integer sid = resultSet.getInt("sid");String name = resultSet.getString("name");Integer age = resultSet.getInt("age");Date birthday = resultSet.getDate("birthday");// 封装Student对象Student student = new Student(sid, name, age, birthday);// 将student对象保存到集合中studentList.add(student);}} catch (Exception e) {e.printStackTrace();} finally {// 释放资源JdbcUtil.close(connection, statement, resultSet);}// 返回集合对象return studentList;}// 条件查询,根据id查询学生信息@Overridepublic Student findById(Integer id) {Student student = new Student();Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {connection = JdbcUtil.getConnection();String sql = "select * from student where sid=?";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setInt(1, id);resultSet = preparedStatement.executeQuery();// 处理结果集while (resultSet.next()) {Integer sid = resultSet.getInt("sid");String name = resultSet.getString("name");Integer age = resultSet.getInt("age");Date birthday = resultSet.getDate("birthday");// 封装Student对象student.setSid(sid);student.setName(name);student.setAge(age);student.setBirthday(birthday);}} catch (SQLException e) {e.printStackTrace();} finally {JdbcUtil.close(connection, statement, resultSet);}return student;}// 添加学生信息@Overridepublic int insert(Student student) {Connection connection = null;Statement statement = null;int result = 0;try {connection = JdbcUtil.getConnection();Date raw_birthday = student.getBirthday();SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");String birthday = simpleDateFormat.format(raw_birthday);String sql = "insert into student (sid, name, age, birthday) values (?, ?, ?, ?)";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setInt(1, student.getSid());preparedStatement.setString(2, student.getName());preparedStatement.setInt(3, student.getAge());preparedStatement.setDate(4, (java.sql.Date) student.getBirthday());result = preparedStatement.executeUpdate();} catch (Exception e) {e.printStackTrace();} finally {JdbcUtil.close(connection, statement);}return result;}// 修改学生信息@Overridepublic int update(Student student) {Connection connection = null;Statement statement = null;int result = 0;try {connection = JdbcUtil.getConnection();Date raw_birthday = student.getBirthday();SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");String birthday = simpleDateFormat.format(raw_birthday);String sql = "UPDATE student SET name=?, age=?, birthday=? WHERE sid=?";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1, student.getName());preparedStatement.setInt(2, student.getAge());preparedStatement.setDate(3, (java.sql.Date) student.getBirthday());result = preparedStatement.executeUpdate();} catch (Exception e) {e.printStackTrace();} finally {JdbcUtil.close(connection, statement);}return result;}@Overridepublic int delete(Integer id) {Connection connection = null;Statement statement = null;int result = 0;try {connection = JdbcUtil.getConnection();String sql = "delete from student where sid=?";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setInt(1, id);result = preparedStatement.executeUpdate();} catch (Exception e) {e.printStackTrace();} finally {JdbcUtil.close(connection, statement);}return result;}
}
6、Service 层
StudentService.java:
package com.service;import com.bean.Student;import java.util.ArrayList;// service 层接口
public interface StudentService {//查询所有学生信息public abstract ArrayList<Student> findAll();//条件查询,根据id获取学生信息public abstract Student findById(Integer id);//新增学生信息public abstract int insert(Student student);//修改学生信息public abstract int update(Student student);//删除学生信息public abstract int delete(Integer id);
}
StudentServiceImpl.java:
package com.service;import com.bean.Student;
import com.dao.StudentDao;
import com.dao.StudentDaoImpl;import java.util.ArrayList;public class StudentServiceImpl implements StudentService {private StudentDao dao = new StudentDaoImpl();// 查询所有学生信息@Overridepublic ArrayList<Student> findAll() {return dao.findAll();}// 根据id查询指定学生信息@Overridepublic Student findById(Integer id) {return dao.findById(id);}// 添加学生信息@Overridepublic int insert(Student student) {return dao.insert(student);}// 修改学生信息@Overridepublic int update(Student student) {return dao.update(student);}// 删除学生信息@Overridepublic int delete(Integer id) {return dao.delete(id);}
}
7、Controller 层
StudentController.java:
package com.controller;import com.bean.Student;
import com.service.StudentService;
import com.service.StudentServiceImpl;
import org.junit.jupiter.api.Test;import java.util.ArrayList;
import java.util.Date;public class StudentController {private StudentService studentService = new StudentServiceImpl();// 查询所有学生信息@Testpublic void findAll() {ArrayList<Student> studentList = studentService.findAll();for (Student student: studentList) {System.out.println(student);}}// 根据id查询指定学生信息@Testpublic void findById() {Student student = studentService.findById(3);System.out.println("查询成功: "+student);}// 添加学生信息@Testpublic void insert() {Student student = new Student(5, "陈七", 19, new Date());int result = studentService.insert(student);if (result != 0) {System.out.println("学生信息添加成功!");} else {System.out.println("学生信息添加失败!");}}// 修改学生信息@Testpublic void update() {Student student = studentService.findById(1);student.setName("xiaoji");int result = studentService.update(student);if (result != 0) {System.out.println("学生信息修改成功!");} else {System.out.println("学生信息修改失败!");}}// 删除学生信息@Testpublic void delete() {int result = studentService.delete(1);if (result != 0) {System.out.println("学生信息删除成功!");} else {System.out.println("学生信息删除失败!");}}
}
8、事务管理
事务一般在 service 层控制管理,因为事务一般与业务耦合,而不是与通用的 dao 层耦合。
- Service 接口:
import java.util.List;public interface UserService {/*** 批量添加* @param users*/void batchAdd(List<User> users); }
- ServiceImpl 实现类:
@Override
public void batchAdd(List<User> users) {// 获取数据库连接Connection connection = JDBCUtils.getConnection();try {// 开启事务connection.setAutoCommit(false);for (User user : users) {// 1.创建ID,并把UUID中的-替换String uid = UUID.randomUUID().toString().replace("-", "").toUpperCase();// 2.给user的uid赋值user.setUid(uid);// 3.生成员工编号user.setUcode(uid);// 手动模拟异常//int n = 1 / 0;// 4.保存userDao.save(connection,user);}// 提交事务connection.commit();}catch (Exception e){try {// 若遇到异常,回滚事务connection.rollback();}catch (Exception ex){ex.printStackTrace();}e.printStackTrace();} finally {JDBCUtils.close(connection,null,null);}
}
八、连接池
1、连接池概述
数据库连接背景:
数据库连接是一种关键的、有限的、昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。
数据库连接池正是针对这种背景提出来的。
数据库连接池:
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。这项技术能解决建立数据库连接耗费资源和时间的问题,明显提高对数据库操作的性能。
数据库连接池原理:
2、开源连接池:C3P0
C3P0 是一个开源的 JDBC 连接池,使用它的开源项目有 Hibernate、Spring等。
使用步骤:
- 导入 jar 包
- 导入配置文件(c3p0-config.xml,文件名不可改)到 src 目录下
- 创建 c3p0 连接池对象
- 获取数据库连接进行使用
使用示例:
- c3p0-config.xml:
<c3p0-config><!-- 使用默认的配置读取连接池对象 --><default-config><!-- 连接参数 --><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/world</property><property name="user">root</property><property name="password">admin</property><!-- 连接池参数 --><!--初始化的连接数量--><property name="initialPoolSize">5</property><!--最大连接数量--><property name="maxPoolSize">10</property><!--超时时间--><property name="checkoutTimeout">3000</property></default-config><!-- 自定义连接池对象 --><named-config name="otherc3p0"> <!-- 连接参数 --><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/world</property><property name="user">root</property><property name="password">admin</property><!-- 连接池参数 --><property name="initialPoolSize">5</property><property name="maxPoolSize">8</property><property name="checkoutTimeout">1000</property></named-config>
</c3p0-config>
- 测试类:
import com.mchange.v2.c3p0.ComboPooledDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class C3P0Test {public static void main(String[] args) throws SQLException {// 创建c3p0连接池对象DataSource dataSource = new ComboPooledDataSource();// 获取数据库连接进行使用Connection con = dataSource.getConnection();// 查询全部学生信息String sql = "SELECT * FROM student";PreparedStatement pst = con.prepareStatement(sql);ResultSet rs = pst.executeQuery();while(rs.next()) {System.out.println(rs.getInt("sid") + "\t" + rs.getString("name") + "\t" + rs.getInt("age") + "\t" + rs.getDate("birthday"));}// 释放资源rs.close();pst.close();con.close(); // 将连接对象归还池中}
}* **优化:抽取工具类**
~~~java
package com.itheima.util;import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;import javax.sql.DataSource;import com.mchange.v2.c3p0.ComboPooledDataSource;public class C3P0Util {// 得到一个数据源private static DataSource dataSource = new ComboPooledDataSource();public static DataSource getDataSource() {return dataSource;}//从数据源中得到一个连接对象public static Connection getConnection(){try {return dataSource.getConnection();} catch (SQLException e) {throw new RuntimeException("服务器错误");}}public static void close(Connection conn, Statement stmt, ResultSet rs){// 关闭资源if(rs!=null){try {rs.close();} catch (Exception e) {e.printStackTrace();}rs = null;}if(stmt!=null){try {stmt.close();} catch (Exception e) {e.printStackTrace();}stmt = null;}if(conn!=null){try {conn.close(); } catch (Exception e) {e.printStackTrace();}conn = null;}}
}
3、开源连接池:Druid
数据库连接池有很多选择,C3P0、DHCP 等,阿里巴巴开源的 druid 作为一名后起之秀,凭借其出色的性能,也逐渐印入了大家的眼帘。
使用步骤:
- 导入 jar 包
- 通过 Properties 集合加载配置文件
- 通过 Druid 连接池工厂类获取数据库连接池对象
- 获取数据库连接,进行使用
示例:
- druid.properties:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/world
username=root
password=itheima
# 初始化连接数量
initialSize=5
# 最大连接数量
maxActive=10
# 超时时间
maxWait=3000
- 测试类:
import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;public class DruidTest {public static void main(String[] args) throws Exception {// 通过Properties集合加载配置文件InputStream is = DruidTest.class.getClassLoader().getResourceAsStream("druid.properties");Properties prop = new Properties();prop.load(is);// 通过Druid连接池工厂类获取数据库连接池对象DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);// 获取数据库连接,进行使用Connection con = dataSource.getConnection();// 查询全部学生信息String sql = "SELECT * FROM student";PreparedStatement pst = con.prepareStatement(sql);ResultSet rs = pst.executeQuery();while(rs.next()) {System.out.println(rs.getInt("sid") + "\t" + rs.getString("name") + "\t" + rs.getInt("age") + "\t" + rs.getDate("birthday"));}// 释放资源rs.close();pst.close();con.close(); // 将连接对象归还池中}
}
- 优化:抽取工具类
public class DataSourceUtils {// 1.私有构造方法private DataSourceUtils(){}// 2.定义DataSource数据源变量private static DataSource dataSource;// 3.提供静态代码块,完成配置文件的加载和获取连接池对象static {try{// 加载配置文件InputStream is = DruidDemo1.class.getClassLoader().getResourceAsStream("druid.properties");Properties prop = new Properties();prop.load(is);// 获取数据库连接池对象dataSource = DruidDataSourceFactory.createDataSource(prop);} catch(Exception e) {e.printStackTrace();}}// 4.提供获取数据库连接的方法public static Connection getConnection() {Connection con = null;try {con = dataSource.getConnection();} catch (SQLException e) {e.printStackTrace();}return con;}// 5.提供获取数据库连接池的方法public static DataSource getDataSource() {return dataSource;}// 6.提供DQL释放资源的方法public static void close(Connection con, Statement stat, ResultSet rs) {if(con != null) {try {con.close();} catch (SQLException e) {e.printStackTrace();}}if(stat != null) {try {stat.close();} catch (SQLException e) {e.printStackTrace();}}if(rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}}// 提供DML释放资源的方法public static void close(Connection con, Statement stat) {close(con, stat, null);}
}
九、DBUtils
1、DBUtils简介
DBUtils其作用是:
DBUtils 是 Apache 开源的 Java 编程中的数据库操作实用工具,小巧简单实用。
DBUtils 封装了对 JDBC 的操作,简化了 JDBC 操作,可以少写代码。
- 对于数据表的读操作,他可以把结果转换成 List、Array、Set 等 Java 集合,便于程序员操作。
- 对于数据表的写操作,也变得很简单(只需写 SQL 语句)。
- 可以使用数据源,使用 JNDI,数据库连接池等技术来优化性能--重用已经构建好的数据库连接对象。
DBUtils 的三个核心对象:
QueryRunner 类
:提供对 SQL 语句操作的 API,它主要有三个方法:- `query():用于执行 select
update()
:用于执行 insert、update、deletebatch()
:批处理
ResultSetHandler 接口
:用于定义 select 操作后怎样封装结果集。DbUtils 类
:一个工具类,定义了关闭资源与事务处理的方法。
2、DBUtils 使用
- 导入 jar 包
- c3p0-0.9.1.2.jar(其他连接对象均可)
- commons-logging-1.1.1.jar
- commons-beanutils-1.8.3.jar
- commons-dbutils-1.4.jar
- 创建 QueryRunner 对象
- 使用 query 方法执行 select 语句
- 使用 ResultSetHandler 封装结果集
- 使用 DbUtils 类释放资源
1)QueryRunner 类(执行对象)
构造函数:
new QueryRunner();
- 其事务可以手动控制。
- 此对象调用的方法(如 query、update、batrch)参数中要有 Connection 对象。
new QueryRunner(DataSource ds);
- 其事务是自动控制的(一个 SQL 一个事务)。
- 此对象调用的方法(如 query、update、batrch)参数中无需 Connection 对象。
示例:
- C3P0Util.java
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;import javax.sql.DataSource;import com.mchange.v2.c3p0.ComboPooledDataSource;public class C3P0Util {// 得到一个数据源private static DataSource dataSource = new ComboPooledDataSource();public static DataSource getDataSource() {return dataSource;}//从数据源中得到一个连接对象public static Connection getConnection(){try {return dataSource.getConnection();} catch (SQLException e) {throw new RuntimeException("服务器错误");}}public static void close(Connection conn, Statement stmt, ResultSet rs){// 关闭资源if(rs!=null){try {rs.close();} catch (Exception e) {e.printStackTrace();}rs = null;}if(stmt!=null){try {stmt.close();} catch (Exception e) {e.printStackTrace();}stmt = null;}if(conn!=null){try {conn.close();} catch (Exception e) {e.printStackTrace();}conn = null;}}}
- DBUtil
import com.bean.Student;
import org.apache.commons.dbutils.QueryRunner;import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;import org.apache.commons.dbutils.ResultSetHandler;
import org.junit.jupiter.api.Test;
import org.apache.commons.dbutils.handlers.BeanListHandler;public class DBUtil {@Testpublic void testDQL1() throws SQLException{//创建一个QueryRunner对象QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());//执行查询语句,并返回结果List<Student> list = qr.query("select * from student where sid=? and name=?", new BeanListHandler<Student>(Student.class), 3, "王五");for (Student student : list) {System.out.println(student);}}@Testpublic void testDQL2() throws SQLException{// 创建一个QueryRunner对象QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());List<Student> list = qr.query("select * from student", new ResultSetHandler<List<Student>>(){// 当query方法执行完select语句,就会将结果集以参数的形式传递进来public List<Student> handle(ResultSet rs) throws SQLException {List<Student> list = new ArrayList<Student>();while(rs.next()){Student student = new Student();student.setSid(rs.getInt(1));student.setName(rs.getString(2));student.setAge(rs.getInt(3));student.setBirthday(rs.getDate(4));list.add(student);}return list;}});for (Student student : list) {System.out.println(student);}}@Testpublic void testDML() throws SQLException{//创建一个QueryRunner对象QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());// 返回影响行数qr.update("insert into student (sid,name,age,birthday) values(?,?,?,?)", "6", "王八", "4", new Date());}@Testpublic void testBatchDQL() throws SQLException{//创建一个QueryRunner对象QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());Object[][] params = new Object[10][]; // 高维代表执行次数;低维代表for (int i=0; i<10; i++) {params[i] = new Object[]{10+i, "菜"+i, 10+i, new Date()};}// 返回影响行数qr.batch("insert into student (sid,name,age,birthday) values(?,?,?,?)", params);}}
2)ResultSetHandler 接口(结果集对象)
ResultSetHandler 下的所有结果处理器:
对象名 | 说明 |
---|---|
ArrayHandler | 适合取 1 条记录。 把该条记录的每列值封装到一个 Object[] 中 |
ArrayListHandler | 适合取多条记录。 把每条记录的每列值封装到一个 Object[] 中,再把数组封装到一个 List 中 |
ColumnListHandler | 取某一列的数据。 把该条记录的每列值封装到 List 中 |
KeyedHandler | 取多条记录。 每一条记录封装到一个 Map 中,再把这个 Map 封装到另外一个 Map 中,key 为指定的字段值 |
MapHandler | 适合取1条记录。 把当前记录的列名和列值放到一个 Map 中 |
MapListHandler | 适合取多条记录。 把每条记录封装到一个 Map 中,再把 Map 封装到 List 中 |
ScalarHandler | 适合取单行单列数据 |
BeanHandler | 取第一行数据 |
BeanListHandler | 将每个数据封装到 List 集合中 |
示例:
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.apache.commons.dbutils.handlers.KeyedHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;import org.junit.Test;public class ResultSetHandler {//ArrayHandler:适合取1条记录。把该条记录的每列值封装到一个数组中Object[]@Testpublic void test1() throws SQLException {QueryRunner qr = new QueryRunner(c3p0.getDataSource());Object[] arr = qr.query("select * from users where id =?",new ArrayHandler(),5);for (Object o : arr) {System.out.println(o);}}//ArrayListHandler:适合取多条记录。把每条记录的每列值封装到一个数组中Object[],把数组封装到一个List中@Testpublic void test2() throws SQLException {QueryRunner qr = new QueryRunner(c3p0.getDataSource());List<Object[]> list = qr.query("select * from users",new ArrayListHandler());for (Object[] o : list) {for(Object os : o){System.out.println(os);}System.out.println("---------");}}//ColumnListHandler:取某一列的数据,封装到List中。@Testpublic void test3() throws SQLException {QueryRunner qr = new QueryRunner(c3p0.getDataSource());//参数的列数是指select语句中列的数List<Object> list = qr.query("select * from users",new ColumnListHandler(3));for (Object o : list) {System.out.println(o);}}//KeyedHandler:取多条记录,每一条记录封装到一个Map中,再把这个Map封装到另外一个Map中,Key为指定的字段值。@Testpublic void test4() throws SQLException {QueryRunner qr = new QueryRunner(c3p0.getDataSource());//参数指大Map的Key,是表中的某列数据,若不重复,则作为输出的记录数量。//小Map中的Key是列名Map<Object,Map<String,Object>> map = qr.query("select * from users",new KeyedHandler(1));for (Map.Entry<Object, Map<String, Object>> m : map.entrySet()) {System.out.println(m.getKey());for(Map.Entry<String, Object> mm : m.getValue().entrySet()){System.out.println(mm.getKey()+":"+mm.getValue());}System.out.println("---------");}}//MapHandler:适合取1条记录。把当前记录的列名和列值放到一个Map中@Testpublic void test5() throws SQLException {QueryRunner qr = new QueryRunner(c3p0.getDataSource());Map<String, Object> map = qr.query("select * from users where id=?",new MapHandler(),6);for (Map.Entry<String, Object> m : map.entrySet()) {System.out.println(m.getKey()+":"+m.getValue());}System.out.println("---------");}//MapListHandler:适合取多条记录。把每条记录封装到一个Map中,再把Map封装到List中@Testpublic void test6() throws SQLException {QueryRunner qr = new QueryRunner(c3p0.getDataSource());List<Map<String, Object>> list = qr.query("select * from users",new MapListHandler());for(Map<String, Object> map : list){for (Map.Entry<String, Object> m : map.entrySet()) {System.out.println(m.getKey()+":"+m.getValue());}System.out.println("---------");}}//ScalarHandler:适合取单行单列数据@Testpublic void test7() throws SQLException {QueryRunner qr = new QueryRunner(c3p0.getDataSource());Object o = qr.query("select username from users",new ScalarHandler(1)); //username列第1个Object o2 = qr.query("select * from users",new ScalarHandler(2)); //第1行第2列Object o3 = qr.query("select count(*) from users",new ScalarHandler());System.out.println(o3);System.out.println(o.getClass().getName());//查看返回的Object变量是什么类型}//BeanHandler:取第一行数据@Testpublic void test8() throws SQLException {QueryRunner qr = new QueryRunner(c3p0.getDataSource());User u = qr.query("select * from users",new BeanHandler<User>(User.class));System.out.println(u);}//BeanListHandler:将每个数据封装到List集合中@Testpublic void test9() throws SQLException {QueryRunner qr = new QueryRunner(c3p0.getDataSource());List<User> list = qr.query("select * from users where id=?", new BeanListHandler<User>(User.class),2);//list若取不到值则返回0,不会有空指针异常的问题for(User user:list){System.out.println(user);}} }
3)ThreadLocal(当前线程对象)
作用:调用该类的 get 方法,永远返回当前线程放入的数据(线程局部变量)。
// 模拟 ThreadLocal 的设计,明白其作用
public class ThreadLocal {private Map<Runnable, Object> container = new HashMap<Runnable, Object>();public void set(Object value){container.put(Thread.currentThread(), value); // 用当前线程作为key}public Object get(){return container.get(Thread.currentThread());}public void remove(){container.remove(Thread.currentThread());}}
案例:
- ThreadLocal 工具类
import java.sql.Connection;
import java.sql.SQLException;public class ManageThreadLocal {private static ThreadLocal<Connection> t1 = new ThreadLocal<Connection>();// 得到当前线程中的一个连接public static Connection getConnection(){Connection conn = t1.get(); // 从当前线程取出一个连接if(conn==null){conn = C3P0Util.getConnection(); // 从池中取出一个t1.set(conn); // 把conn对象放入到当前线程对象中}return conn;}// 开始事务public static void startTransaction(){try {getConnection().setAutoCommit(false); // 从当前线程对象中取出的连接,并开始事务} catch (SQLException e) {e.printStackTrace();}}// 提交事务public static void commit(){try {getConnection().commit();} catch (SQLException e) {e.printStackTrace();}}// 回滚事务public static void rollback(){try {getConnection().rollback();} catch (SQLException e) {e.printStackTrace();}}public static void close(){try {getConnection().close(); // 把连接放回池中t1.remove(); // 把当前线程对象中的conn移除} catch (SQLException e) {e.printStackTrace();}}}
- AccountDaoImpl
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import com.dao.AccountDao;
import com.domain.Account;
import com.util.C3P0Util;
import com.util.ManagerThreadLocal;public class AccountDaoImpl implements AccountDao {public void updateAccount(String fromname, String toname, double money) throws Exception {// 创建一个QueryRunner对象QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());qr.update("update account set money=money-? where name=?",money,fromname);qr.update("update account set money=money+? where name=?",money,toname);}public void updateAccout(Account account) throws Exception {QueryRunner qr = new QueryRunner();return qr.update(ManagerThreadLocal.getConnection(),"update account set money=? where name=?",account.getMoney(),account.getName());}public Account findAccountByName(String name) throws Exception {QueryRunner qr = new QueryRunner();return qr.query(ManagerThreadLocal.getConnection(),"select * from account where name=?", new BeanHandler<Account>(Account.class),name);}}
- AccountServiceImpl
import java.sql.Connection;
import java.sql.SQLException;
import com.dao.AccountDao;
import com.dao.impl.AccountDaoImpl;
import com.domain.Account;
import com.service.AccountService;
import com.util.C3P0Util;
import com.util.ManagerThreadLocal;public class AccountServiceImpl implements AccountService {public void transfer(String fromname, String toname, double money) {// ad.updateAccount(fromname, toname, money);AccountDao ad = new AccountDaoImpl();try {ManagerThreadLocal.startTransacation(); // begin// 分别得到转出和转入账户对象Account fromAccount = ad.findAccountByName(fromname);Account toAccount = ad.findAccountByName(toname);// 修改账户各自的金额fromAccount.setMoney(fromAccount.getMoney()-money);toAccount.setMoney(toAccount.getMoney()+money);//完成转账操作ad.updateAccout(fromAccount);
// int i = 10/0;ad.updateAccout(toAccount);ManagerThreadLocal.commit(); // 提交事务} catch (Exception e) {try {ManagerThreadLocal.rollback(); // 回滚事务} catch (Exception e1) {e1.printStackTrace();}}finally{try {ManagerThreadLocal.close();} catch (Exception e) {e.printStackTrace();} // 关闭}}}