详解JDBC使用
什么是JDBC
JDBC(JavaDatabaseConnectivity),即Java数据库连接,是一种用于执行SQL语句的JavaAPI,可以为多种关系数据库提供同一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,根据这种基准可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。总而言之,JDBC做了三件事:
1、与数据库建立连接
2、发送操作数据库的语句
3、处理结果
JDBC简单示例
下面的代码演示了如何利用JDBC从数据库中查询若干条符合要求的数据出来,使用的数据库是MySql。
1、建立一个数据库和一张表,我的习惯是在CLASSPATH底下建立一个.sql的文件用于存放sql语句
createdatabaseschool; useschool; createtablestudent ( studentIdintprimarykeyauto_incrementnotnull, studentNamevarchar(10)notnull, studentAgeint, studentPhonevarchar(15) ) insertintostudentvalues(null,'Betty','20','00000000'); insertintostudentvalues(null,'Jerry','18','11111111'); insertintostudentvalues(null,'Betty','21','22222222'); insertintostudentvalues(null,'Steve','27','33333333'); insertintostudentvalues(null,'James','22','44444444'); commit;
2、建立一个.properties文件用于存储MySql连接的几个属性。为什么要建立.properties而不在代码里面写死,由于这个并不是Java设计模式的分类,就不细讲了,只需要记住:从设计的角度看,把内容写在配置文件中永远好过把内容写死在代码中。
mysqlpackage=com.mysql.jdbc.Driver mysqlurl=jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf-8 mysqlname=root mysqlpassword=root
3、根据表字段建立实体类
publicclassStudent
{
privateintstudentId;
privateStringstudentName;
privateintstudentAge;
privateStringstudentPhone;
publicStudent(intstudentId,StringstudentName,intstudentAge,
StringstudentPhone)
{
this.studentId=studentId;
this.studentName=studentName;
this.studentAge=studentAge;
this.studentPhone=studentPhone;
}
publicintgetStudentId()
{
returnstudentId;
}
publicStringgetStudentName()
{
returnstudentName;
}
publicintgetStudentAge()
{
returnstudentAge;
}
publicStringgetStudentPhone()
{
returnstudentPhone;
}
publicStringtoString()
{
return"studentId="+studentId+",studentName="+studentName+",studentAge="+
studentAge+",studentPhone="+studentPhone;
}
}
4、写一个DBConnection类专门用于向外提供数据库连接。我这里用了MySql,所以只有一个mysqlConnection,如果还用到了Oracle,当然还可以向外提供一个oracleConnection。把这些连接设为全局的可能有人会想是否会有线程安全问题,这是一个很好的问题。那因为我们只从Connection里面读取一个PreparedStatement出来,而不会去写它,只读不修改,是不会引发线程安全问题的。另外把Connection设置为static的保证了Connection在内存中只有一份,不会占多大资源,每次使用完不调用close()方法去关闭它也没事。
publicclassDBConnection
{
privatestaticPropertiesproperties=newProperties();
static
{
/**要从CLASSPATH下取.properties文件,因此要加"/"*/
InputStreamis=DBConnection.class.getResourceAsStream("/db.properties");
try
{
properties.load(is);
}
catch(IOExceptione)
{
e.printStackTrace();
}
}
/**这个mysqlConnection只是为了用来从里面读一个PreparedStatement,不会往里面写数据,因此没有线程安全问题,可以作为一个全局变量*/
publicstaticConnectionmysqlConnection=getConnection();
publicstaticConnectiongetConnection()
{
Connectioncon=null;
try
{
Class.forName((String)properties.getProperty("mysqlpackage"));
con=DriverManager.getConnection((String)properties.getProperty("mysqlurl"),
(String)properties.getProperty("mysqlname"),
(String)properties.getProperty("mysqlpassword"));
}
catch(ClassNotFoundExceptione)
{
e.printStackTrace();
}
catch(SQLExceptione)
{
e.printStackTrace();
}
returncon;
}
}
5、建立一个工具类,用来写各种方法,专门和数据库进行交互。这种工具类最好搞成单例的,这样就不用每次去new出来了(实际上new出来也没看出来会有什么好处),节省资源
packagecom.xrq.test11;
importjava.sql.Connection;
importjava.sql.PreparedStatement;
importjava.sql.ResultSet;
importjava.util.ArrayList;
importjava.util.List;
publicclassStudentManager
{
privatestaticStudentManagerinstance=newStudentManager();
privateStudentManager()
{
}
publicstaticStudentManagergetInstance()
{
returninstance;
}
publicListquerySomeStudents(StringstudentName)throwsException
{
ListstudentList=newArrayList();
Connectionconnection=DBConnection.mysqlConnection;
PreparedStatementps=connection.prepareStatement("select*fromstudentwherestudentName=?");
ps.setString(1,studentName);
ResultSetrs=ps.executeQuery();
Studentstudent=null;
while(rs.next())
{
student=newStudent(rs.getInt(1),rs.getString(2),rs.getInt(3),rs.getString(4));
studentList.add(student);
}
ps.close();
rs.close();
returnstudentList;
}
}
6、写个main函数去调用一下
ListstudentList=newArrayList (); studentList=StudentManager.getInstance().querySomeStudents("Betty"); for(Studentstudent:studentList) System.out.println(student);
7、看一下运行结果,和数据库里面的一样,成功
studentId=1,studentName=Betty,studentAge=20,studentPhone=00000000 studentId=3,studentName=Betty,studentAge=21,studentPhone=22222222
为什么要使用占位符"?"
看一下第5点,大家一定注意到了,写sql语句的时候用了"?"占位符,当然有美化代码的因素,不用占位符就要在括号里写"+"来拼接参数,如果要拼接的参数一多,代码肯定不好看,可读性不强。但是除了这个原因,还有另外一个重要的原因,就是避免一个安全问题。假设我们不用占位符写sql语句,那"querySomeStudents(Stringname)throwsException"方法就要这么写:
publicListquerySomeStudents(StringstudentName)throwsException { List studentList=newArrayList (); Connectionconnection=DBConnection.mysqlConnection; PreparedStatementps=connection.prepareStatement("select*fromstudentwherestudentName='"+studentName+"'"); ResultSetrs=ps.executeQuery(); Studentstudent=null; while(rs.next()) { student=newStudent(rs.getInt(1),rs.getString(2),rs.getInt(3),rs.getString(4)); studentList.add(student); } ps.close(); rs.close(); returnstudentList; }
上面的main函数一样可以获取到两条数据,但是问题来了,如果我这么调用呢:
publicstaticvoidmain(String[]args)throwsException
{
ListstudentList=newArrayList();
studentList=StudentManager.getInstance().querySomeStudents("'or'1'='1");
for(Studentstudent:studentList)
System.out.println(student);
}
看下运行结果:
studentId=1,studentName=Betty,studentAge=20,studentPhone=00000000 studentId=2,studentName=Jerry,studentAge=18,studentPhone=11111111 studentId=3,studentName=Betty,studentAge=21,studentPhone=22222222 studentId=4,studentName=Steve,studentAge=27,studentPhone=33333333 studentId=5,studentName=James,studentAge=22,studentPhone=44444444
为什么?看下拼接之后的sql语句就知道了:
select*fromstudentwherestudentName=''or'1'='1'
'1'='1'永远成立,所以前面的查询条件是什么都没用。这种问题是有应用场景的,不是随便写一下。Java越来越多的用在Web上,既然是Web,那么查询的时候有一种情况就是用户输入一个条件,后台获取到查询条件,拼接sql语句查数据库,有经验的用户完全可以输入一个"‘''or'1'='1",这样就拿到了库里面的所有数据了。
JDBC事物
谈数据库必然离不开事物,事物简单说就是"要么一起成功,要么一起失败"。那简单往前面的StudentManager里面写一个插入学生信息的方法:
publicvoidaddStudent(StringstudentName,intstudentAge,StringstudentPhone)throwsException
{
Connectionconnection=DBConnection.mysqlConnection;
PreparedStatementps=connection.prepareStatement("insertintostudentvalues(null,?,?,?)");
ps.setString(1,studentName);
ps.setInt(2,studentAge);
ps.setString(3,studentPhone);
if(ps.executeUpdate()>0)
System.out.println("添加学生信息成功");
else
System.out.println("添加学生信息失败");
}
publicstaticvoidmain(String[]args)throwsException
{
StudentManager.getInstance().addStudent("Betty",17,"55555555");
}
运行就不运行了,反正最后结果是"添加学生信息成功",数据库里面多了一条数据。注意一下:
1、增删改用的是executeUpdate()方法,因为增删改认为都是对数据库的更新
2、查询用的是executeQuery()方法,看名字就知道了"Query",查询嘛
可能有人注意到一个问题,就是Java代码在insert后并没有对事物进行commit,数据就添加进数据库了,也能查出来,这是为什么呢?因为JDK的Connection设置了事物的自动提交。如果在addStudent(...)方法里面这么写:
Connectionconnection=DBConnection.mysqlConnection; connection.setAutoCommit(false);
autoCommit这个属性原来是true,JDK自然会帮助开发者自动提交事物了。OK,如果要改成手动提交事物的代码,那么应该这么写addStudent(...)方法:
publicvoidaddStudent(StringstudentName,intstudentAge,StringstudentPhone)throwsException
{
Connectionconnection=DBConnection.mysqlConnection;
connection.setAutoCommit(false);
PreparedStatementps=connection.prepareStatement("insertintostudentvalues(null,?,?,?)");
ps.setString(1,studentName);
ps.setInt(2,studentAge);
ps.setString(3,studentPhone);
try
{
ps.executeUpdate();
connection.commit();
}
catch(Exceptione)
{
e.printStackTrace();
connection.rollback();
}
}
要记得抛异常的时候利用rollback()方法回滚掉事物。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持毛票票!