技术标签: JDBC MySQL连接 PreparedStatement和Statement的异同 execute和executeUpdate的区别 数据库
目录
3.创建Statement或者PreparedStatement接口,执行SQL语句
Statement和PreparedStatement的异同及优缺点
笔者花了一整天的时间仔细研究了JDBC的使用,内容很充实,代码也都经过了验证。如果你能够仔细阅读完这篇文章,JDBC的相关知识我想你一定会有所掌握。在阅读的过程中,有任何不理解的地方都欢迎留言讨论。
JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个规范而不是一个实现,能够执行SQL语句。它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现,本文中的代码都是针对MySQL数据库实现的。
访问MySQL数据库需要用到第三方的类,这些第三方的类,都被压缩在一个.Jar的文件里。mysql-connector-java-5.0.8-bin.jar包可以在网上下载,或者在MySQL的安装目录下找到。通常下载到该jar包之后将其放到在项目的lib目录下,在本例就会放在E:\project\j2se\lib 这个位置,然后在eclipse中导入这个jar包。
导包步骤: 右键project->property->java build path->libaries->add external jars
如果没有完成上述步骤的导包操作,后面会抛出ClassNotFoundException
初始化驱动
通过初始化驱动类com.mysql.jdbc.Driver,该类就在 mysql-connector-java-5.0.8-bin.jar中。如果你使用的是oracle数据库那么该驱动类将不同。
注意:Class.forName需要捕获ClassNotFoundException.
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class.forName是把这个类加载到JVM中,加载的时候,就会执行其中的静态初始化块,完成驱动的初始化的相关工作。
这里需要提供:数据库服务端的IP地址:127.0.0.1 (这是本机,如果连接其他电脑上的数据库,需填写相应的IP地址)
数据库的端口号: 3306 (mysql专用端口号)
数据库名称 exam(根据你自己数据库中的名称填写)
编码方式 UTF-8
账号 root
密码 admin(如果你在创建数据库的时候没有使用默认的账号和密码,请填写自己设置的账号和密码)
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8", "root", "admin");
Connection是与特定数据库连接回话的接口,使用的时候需要导包,而且必须在程序结束的时候将其关闭。getConnection方法也需要捕获SQLException异常。
因为在进行数据库的增删改查的时候都需要与数据库建立连接,所以可以在项目中将建立连接写成一个工具方法,用的时候直接调用即可:
/**
* 取得数据库的连接
* @return 一个数据库的连接
*/
public static Connection getConnection(){
Connection conn = null;
try {
//初始化驱动类com.mysql.jdbc.Driver
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8","root", "admin");
//该类就在 mysql-connector-java-5.0.8-bin.jar中,如果忘记了第一个步骤的导包,就会抛出ClassNotFoundException
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
Statement接口创建之后,可以执行SQL语句,完成对数据库的增删改查。其中 ,增删改只需要改变SQL语句的内容就能完成,然而查询略显复杂。在Statement中使用字符串拼接的方式,该方式存在句法复杂,容易犯错等缺点,具体在下文中的对比中介绍。所以Statement在实际过程中使用的非常的少,所以具体的放到PreparedStatement那里给出详细代码。
字符串拼接方式的SQL语句是非常繁琐的,中间有很多的单引号和双引号的混用,极易出错。
Statement s = conn.createStatement();
// 准备sql语句
// 注意: 字符串要用单引号'
String sql = "insert into t_courses values(null,"+"'数学')";
//在statement中使用字符串拼接的方式,这种方式存在诸多问题
s.execute(sql);
System.out.println("执行插入语句成功");
与 Statement一样,PreparedStatement也是用来执行sql语句的与创建Statement不同的是,需要根据sql语句创建PreparedStatement。除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接。
给数据库中添加课程: (以下代码中最后关闭资源的两个方法 DbUtil.close(pstmt); DbUtil.close(conn); 和上面的建立连接的方法是一样的,是在工具类中定义了的关闭方法,下文会给出其代码)
/**
* 添加课程
* @param courseName 课程名称
*/
public void addCourse(String courseName){
String sql = "insert into t_course(course_name) values(?)";
//该语句为每个 IN 参数保留一个问号(“?”)作为占位符
Connection conn = null; //和数据库取得连接
PreparedStatement pstmt = null; //创建statement
try{
conn = DbUtil.getConnection();
pstmt = (PreparedStatement) conn.prepareStatement(sql);
pstmt.setString(1, courseName); //给占位符赋值
pstmt.executeUpdate(); //执行
}catch(SQLException e){
e.printStackTrace();
}
finally{
DbUtil.close(pstmt);
DbUtil.close(conn); //必须关闭
}
}
对数据库中的课程进行删除:
/**
* 删除课程
* @param courseId
*/
public void delCourse(int courseId){
String sql = "delete from t_course where course_id = ?";
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = DbUtil.getConnection();
pstmt = (PreparedStatement) conn.prepareStatement(sql);
pstmt.setInt(1, courseId);
pstmt.executeUpdate();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}finally{
DbUtil.close(pstmt);
DbUtil.close(conn); //必须关闭
}
}
对数据库中的课程进行修改:
/**
* 修改课程
* @param courseId
* @param courseName
*/
public void modifyCourse(int courseId,String courseName){
String sql = "update t_course set course_name =? where course_id=?";
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = DbUtil.getConnection();
pstmt = (PreparedStatement) conn.prepareStatement(sql);
pstmt.setString(1, courseName); //利用Preparedstatement的set方法给占位符赋值
pstmt.setInt(2, courseId);
pstmt.executeUpdate();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}finally{
DbUtil.close(pstmt);
DbUtil.close(conn); //必须关闭
}
}
由上面的增删改程序可以看出,他们的代码都是大同小异的,主要是SQL语句存在差异,其他的地方几乎是完全相同的。其中有几个地方需要注意:
String sql = "update t_course set course_name =? where course_id=?";
后面需要用到PreparedStatement接口创建的pstmt的set方法给占位符进行赋值。注意一点,这里的参数索引是从1开始的。
pstmt = (PreparedStatement) conn.prepareStatement(sql);
pstmt.setString(1, courseName); //利用Preparedstatement的set方法给占位符赋值
pstmt.setInt(2, courseId);
pstmt.executeUpdate();
增删改都使用pstmt.executeUpdate();语句进行SQL语句的提交 ,下文的查询会有所不同,请注意。
for(int i=1;i<100;i++){
pstmt.setInt(1,8000+i);
pstmt.setString(2,"赵_"+i);
pstmt.addBatch();
//批量更新
if(i%10==0){
pstmt.executeBatch();
}
}
下面我们来看稍显麻烦一点的查询操作:
/**
* 查询课程
* @return
*/
public List<Course> findCourseList(){
String sql = "select * from t_course order by course_id";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
//创建一个集合对象用来存放查询到的数据
List<Course> courseList = new ArrayList<>();
try {
conn = DbUtil.getConnection();
pstmt = (PreparedStatement) conn.prepareStatement(sql);
rs = (ResultSet) pstmt.executeQuery();
while (rs.next()){
int courseId = rs.getInt("course_id");
String courseName = rs.getString("course_name");
//每个记录对应一个对象
Course course = new Course();
course.setCourseId(courseId);
course.setCourseName(courseName);
//将对象放到集合中
courseList.add(course);
}
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}finally{
DbUtil.close(pstmt);
DbUtil.close(conn); //必须关闭
}
return courseList;
}
查询操作使用executeQuery()进行更新。其他相关的问题放在第四步(处理和显示结果)中解释。
执行查询语句,并把结果集返回给集合ResultSet
ResultSet rs = s.executeQuery(sql);
利用While(ResultSet.next()){
…}循环将集合ResultSet中的结果遍历出来。
ResultSet.getXX(); 这里的get方法的括号里面可以填属性值,如下图代码中的course_id,还可以填该属性在数据表中的列号,从1开始编码,例如:course_id在我的t-courses数据表中位于第一列,所以执行get方法的时候,我除了代码段中写法外,还可以这样写int courseId = rs.getInt(1);但是不推荐使用列号的这种方式,因为一段数据表中个属性值得顺序发生变化,就会导致这里出错,而使用属性名则不会出现这样的问题。
while (rs.next()){
int courseId = rs.getInt("course_id");
String courseName = rs.getString("course_name");
//每个记录对应一个对象
Course course = new Course();
//在我的项目中创建了course类,其中定义了set方法,所以这里将查询到的值传给了course,也可以直接打印到控制台
course.setCourseId(courseId);
course.setCourseName(courseName);
//将对象放到集合中
courseList.add(course);
}
还有一点需要说明的是:
因为在我的项目中创建了course类,其中定义了set方法,所以这里将查询到的值传给了course,你也可以直接用打印语句将CourseId和CourseName打印到控制台。
course.setCourseId(courseId);
course.setCourseName(courseName);
在JDBC编码的过程中我们创建了Connection、ResultSet等资源,这些资源在使用完毕之后是一定要进行关闭的。关闭的过程中遵循从里到外的原则。因为在增删改查的操作中都要用到这样的关闭操作,为了使代码简单,增加其复用性,这里我将这些关闭的操作写成一个方法和建立连接的方法一起放到一份工具类中。
/**
* 封装三个关闭方法
* @param pstmt
*/
public static void close(PreparedStatement pstmt){
if(pstmt != null){ //避免出现空指针异常
try{
pstmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
public static void close(Connection conn){
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
public static void close(ResultSet rs){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
JDBC编程的内容就这些了,如果你已经全部掌握,还有事务、获取自增、获取元数据、ORM、DAO、数据连接池等内容可以自行了解一下。
另外,Statement和PreparedStatement的异同,execute和executeUpdate的区别等内容,这里做一些介绍。
同:两者都是用来执SQL语句的
异:PreparedStatement需要根据SQL语句来创建,它能够通过设置参数,指定相应的值,不是像Statement那样使用字符串拼接的方式。
PreparedStatement的优点:
1、其使用参数设置,可读性好,不易记错。在statement中使用字符串拼接,可读性和维护性比较差。
2、其具有预编译机制,性能比statement更快。
3、其能够有效防止SQL注入攻击。
相同点:二者都能够执行增加、删除、修改等操作。
不同点:
1、execute可以执行查询语句,然后通过getResult把结果取出来。executeUpdate不能执行查询语句。
2、execute返回Boolean类型,true表示执行的是查询语句,false表示执行的insert、delete、update等。executeUpdate的返回值是int,表示有多少条数据受到了影响。
关于JDBC的介绍就这么多了,欢迎评论区讨论。
文章浏览阅读461次。本次教程用宇宙模拟器space engine 0.980版本教大家如何创建自定义太阳系系统,包括创建恒星,行星,卫星,小行星,彗星等天体,最后教如何制作插件,打包压缩成pak文件。把链接复制到浏览器地址栏上,按回车键就可以看了关于宇宙模拟器Space Engine的天体(星球,星系,星云,星团等)脚本参数,如何创建天体(星球,星系,星云,星团等)的脚本,请看这些教程space engine打包创建..._space engine
文章浏览阅读10w+次,点赞5次,收藏17次。更新了Chrome,发现网页全部打不开了。都显示:“喔唷 崩溃啦”。点击任何按钮,例如,设置,选项,历史记录等,也“喔唷 崩溃啦” 卸载重装也无效解决方案找到 路径C:\Windows\System32\drivers\bd0001.sys删除(没有强制删除工具重命名也行,随便改个名字)重启电脑chrome就正常了原因可能是注册列表被一些卫士类优化工具或杀毒..._为什么googlechrome设置也打不开
文章浏览阅读1.5k次。安装thrift,我是mac机器,mac下使用homebrew安装thrift很方便,具体看另一篇博客。首先的定义接口文件: service HelloWorldService { string sayHello(1:string username)}因为thrift是支持跨平台的,所以这里接口的定义,thrift定义了自己的规范。 然后使用thrift的工具..._thrift调用例子 java
文章浏览阅读261次。我不确定术语“通配符”是否可以解释我的观点,但有时在一些现成的脚本中,我们可以调用一个非定义的函数,如find_by_age(23),其中age可以是映射到数据库表记录的任何其他内容.所以我可以调用find_by_name,find_by_email,find_by_id等等.那么我们怎么能以程序或面向对象的方式做这样的事情呢?解决方法:你正在寻找的术语是魔法.基本上是这样的:class Foo ..._php路径通配符
文章浏览阅读904次。ANR-WatchDog-ohos一个简单的监测程序,可检测到鸿蒙系统的 ANR(Application Not Response-应用程序无响应)错误并引发有意义的异常项目名称:ANR-WatchDog-ohos所属系列:鸿蒙的第三方组件适配移植功能:可检测到鸿蒙系统的ANR错误并引发有意义的异常项目移植状态:全部完成调用差异:无开发版本:sdk5,DevEco Studio2.1 beta3项..._com.github.anrwatchdog.anrerror: application not responding for at least 400
文章浏览阅读441次。赵辉《Visual+C++_MATLAB图像处理与识别实用案例精选》程序代码说明P0201:MATLAB赋值P0202:MATLAB中的for循环P0203:MATLAB中的for循环和if条件P0205:MATLAB图像处理的基本操作P0206:MATLAB高级图像处理操作P0207:根据RGB图像创建一幅灰度图像P0208:二值图像的取反操作P0209:用imshow函数显示图像P0210:在..._matlab命令行检测网络
文章浏览阅读3.1k次。经常会有机友提问,乐视MAX(乐视 X900+)手机支不支持一键刷机?由于奇兔刷机已经支持多达上千款安卓手机一键刷机,所以有时候小编也无法及时回答上来,最简单的办法就是把手机连上奇兔刷机,即可看到手机是否支持一键刷机。一键刷机并非每次都能一次成功,如果一次不成功可以多试几次,刷机失败不会对您的手机造成任何影响。奇兔市场小编给大家分享下乐视MAX(乐视 X900+)一键刷机教程,虽然使用方法很简单,..._乐视x900刷miui10
文章浏览阅读2.5k次,点赞7次,收藏49次。文章目录1.创建数据库与表2. 创建java项目并且导入jar包(buildPath)2.1 创建数据库连接文件 DBhelper.java2.2 创建vo (stu.java), 与数据库一致2.3 创建service (StuService.java)3. 编写功能类 FromDbToExcel.java注意事项:4. 编写功能类 FromExcelToDb.java注意事项5. 效果展示1.创建数据库与表2. 创建java项目并且导入jar包(buildPath)2.1 创建数据库连接文件_mysql导出多张表结构 excel java代码
文章浏览阅读352次。首先介绍阻塞与非阻塞:阻塞是个什么概念呢?比如某个时候你在等快递,但是你不知道快递什么时候过来,而且你没有别的事可以干(或者说接下来的事要等快递来了才能做);那么你可以去睡觉了,因为你知道快递把货送来时一定会给你打个电话(假定一定能叫醒你)。非阻塞忙轮询。接着上面等快递的例子,如果用忙轮询的方法,那么你需要知道快递员的手机号,然后每分钟给他挂个电话:“你到了没?”很明显一般人不会用第二..._epoll kqueue
文章浏览阅读509次。关于西门子PLC modbus通讯与运动控制的应用应用的设备昆仑通态1570gi(15寸)西门子plc smart-st20昆仑通态参数的设置与组态的重点组态软件选用型号1570gi的屏幕设置通讯地址 192.168.190报警参数的配置脚本的编写关于组态过程中遇到的问题由于这是我参加工作自己独立的完成的,这个期间真的是太坎坷了,我来说说我遇到的问题吧脚本应用if和if的嵌套IF THEN IF THEN ENDIFENDIF由于画面中关联着报_plc作为从站 modbus协议和运动控制命令冲突
文章浏览阅读972次。先用一台电脑连接IPSAN管理端口进去配置阵列配置完成后把IPSAN的ISCSI接口连接到服务器网口,man iscsiadm 查看可以查看是否安装了 iscsiadmyum -y install*iscsi* 命令安装iscsiadm然后在服务器上输入:iscsiadm -m discovery -t sendtargets -p 169.254.194.201:3260 (169.254.19..._centos实现ipsan卸载
文章浏览阅读698次。点击上方 "程序员小乐"关注,星标或置顶一起成长后台回复“大礼包”有惊喜礼包!关注订阅号「程序员小乐」,收看更多精彩内容每日英文Tough people aren..._cannot use range access on index 'env_index' due to type or collation conver