JDBC概述
JDBC原理
JDBC开发步骤
获得链接
获得语句执行平台
Statement:有sql注入危险
PreparedStatement:防止sql注入:
常用方法
结果处理:ResultSet
释放资源
JDBC工具类
在配置文件properties中设置数据库
练习
jdbc基础
JDBC概述
JDBC
(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问(不同数据库是有差异的,用jdbc都可以访问,相当于屏蔽了不同数据库之间的差异),它由一组用Java语言编写的类和接口组成。是Java访问数据库的标准规范
JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。(JDBC使用者不能自己写实现类,只能使用接口)
JDBC需要连接驱动(mysql-connector-java-5.1.39-bin.jar
),驱动是两个设备要进行通信(的必要软件),满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。
驱动程序:是一套实现类,满足sun公司给的接口,满足一定通信数据格式;
开发的时候,只需要关注接口就行,使用的时候,会自动调用的
JDBC定义:是一套API,是sun定义类或是接口;
JDBC原理
Java
提供访问数据库规范称为JDBC
,而生产厂商提供规范的实现类称为驱动。
驱动是类库,实现了sun规定的接口,
驱动程序类库,实现接口,重写方法
JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,从而不能操作数据库!每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生成厂商提供。
JDBC开发步骤
- 注册驱动.
告知JVM使用的是哪一个数据库的驱动 - 获得连接.
使用JDBC中的类,完成对MySQL数据库的连接(效率低) - 获得语句执行平台
通过连接对象获取对SQL语句的执行者对象 - 执行sql语句
使用执行者对象,向数据库执行SQL语句
获取到数据库的执行后的结果 - 处理结果
- 释放资源.
一堆close()
public class jdbcTest {
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver"); //加载驱动程序
String connString = "jdbc:mysql://localhost:3306/db_database17";//"jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=utf8"
Connection conn = DriverManager.getConnection(connString,"root","123456");// 数据库的连接,有时帐号,密码
Statement stat = conn.createStatement();// 得到一个Statement(不带参数的简单语),将SQL语句发送到数据库
stat.executeUpdate("create table if not exists people (id char(10), name char(20), age int, gender bit ) ;");//创建表people(字段id...)
stat.executeUpdate("insert into people values ('001', 'Tom', 18, 1);");//插入内容
stat.executeUpdate("insert into people values ('002', 'Marry', 20, 0);");
stat.executeUpdate("insert into people values ('003', 'Peter', 25, 1);");
stat.executeUpdate("update people set age=age+1 where id='003';");//更新、修改数据
String sql = "select * from people;";//查询,在people中查询*
ResultSet rs = stat.executeQuery(sql);//executeQuery执行查询,结果导入result,
while (rs.next()) { //循环遍历,得到每一个记录,字段
String name = rs.getString("name");
int age = rs.getInt("age");
boolean gender = rs.getBoolean(4);
System.out.printf("name = %s; age = %d,gender =%s\n", name, age, (gender ? "male" : "female"));
}
rs.close();//查询结束
conn.close();
}
}
bit
称为位数据类型,长度为1位,有两种取值:0和1,在输入0以外的其他值时,系统均把它们当1看待。这种数据类型常作为逻辑变量使用,用来表示真、假或是、否等二值选择。程序读取数据库出来之后的表现形式是true或者false,但是保存在数据库中的结构类型是0或者1,1表示true,0表示false。gender bit
,insert时是0或1,读取时是:(gender?"true":"false"),1—true,0—false
获得链接
- 方法1:
Connection con = DriverManager.getConnection(“jdbc:mysql://localhost:3306/数据库名”,”root”,”root”);
//有中文的时候,URL是:"jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=utf8"
获得语句执行平台
Statement:有sql注入危险
获取Statement对象,将SQL语句发送到数据库,返回值是 Statement接口的实现类对象,,在mysql驱动程序
Statement stat = con.createStatement();
PreparedStatement:防止sql注入:
登录案例:
假设有登录案例SQL语句如下:
SELECT * FROM 用户表 WHERE NAME = 用户输入的用户名 AND PASSWORD = 用户输的密码;
此时,当用户输入正确的账号与密码后,查询到了信息则让用户登录。但是当用户输入的账号为XXX 密码为:XXX' OR 'a'='a'时,则真正执行的代码变为:
SELECT * FROM 用户表 WHERE NAME = 'XXX' AND PASSWORD ='XXX' OR 'a'='a';
此时,上述查询语句时永远可以查询出结果的。那么用户就直接登录成功了,显然我们不希望看到这样的结果,这便是SQL注入问题。
为此,我们使用PreparedStatement来解决对应的问题。
//定义
PreparedStatement pst = con.prepareStatement(sql);
- 设置参数:
setObject();
setInt();
setString();
等...
String sql = "SELECT * FROM users WHERE uname =? AND uaddress =?";
//调用Connection接口的方法prepareStatement,获取PrepareStatement接口的实现类
//方法中参数,SQL语句中的参数全部采用问号占位符
PreparedStatement pst = con.prepareStatement(sql);
//调用pst对象set方法,设置问号占位符上的参数
pst.setObject(1, user);
pst.setString(2, pass);
//调用方法,执行SQL,获取结果集
ResultSet rs = pst.executeQuery();
while(rs.next()){
System.out.println(rs.getString("uname ")+" "+rs.getString("uaddress "));
}
常用方法
方法 | 定义 |
---|---|
int executeUpdate(String sql); | 执行执行数据库中的SQL语,insert update delete 语句 返回值int,操作成功数据表多少行的行数 |
ResultSet executeQuery(String sql); | 执行select 语句. |
boolean execute(String sql); | 执行select 返回true 执行其他的语句返回false |
结果处理:ResultSet
ResultSet实际上就是一张二维的表格,我们可以调用其boolean next()方法指向某行记录,当第一次调用next()方法时,便指向第一行记录的位置,这时就可以使用ResultSet提供的getXXX(int col)方法(与索引从0开始不同个,列从1开始)来获取指定列的数据:
- 获取一行数据:
rs.next();
- 获取列数据:
方法 | 定义(参数是int:第i列,参数是String:根据字段名获取内容) |
---|---|
Object getObject(int index) / Object getObject(String name) | 获得对象 |
String getString(int index) / Object getObject(String name) | 获得字符串 |
int getInt(int index) / Object getObject(String name) | 获得整形 |
double getDouble(int index) / Object getDouble(String name) | 获得双精度浮点型 |
其他... |
释放资源
与IO流一样,使用后的东西都需要关闭!关闭的顺序是先得到的后关闭,后得到的先关闭。
rs.close();
stmt.close();
con.close();
JDBC工具类
“获得数据库连接”操作,将在以后的增删改查所有功能中都存在,可以封装工具类JDBCUtils。提供获取连接对象的方法,从而达到代码的重复利用。
该工具类提供方法:public static Connection getConnection ()。
/*
* JDBC工具类
*/
public class JDBCUtils {
//构造方法私有,防止别人创建
private JDBCUtils(){}
privatestatic Connection con ;
static{
try{
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/mybase";
String username="root";
String password="123";
con = DriverManager.getConnection(url, username, password);
}catch(Exception ex){
//若连接失败,直接抛出
throw new RuntimeException(ex+"数据库连接失败");
}
}
/*
* 定义静态方法,返回数据库的连接对象
*/
public static Connection getConnection(){//连接数据库
return con;
}
public static void close(Connection con,Statement stat){//关闭数据库
closeStatement(stat);
closeConn(conn);
}
public static void close(Connection con,Statement stat , ResultSet rs){
closeResultSet(rs);
close(con,stat)
}
/**
* 释放连接
* @param conn 连接
*/
public static void closeConn(Connection conn){
if(conn!=null){//空的时候,不用关闭
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn=null;//对象为空的时候,垃圾回收快
}
}
/**
* 释放语句执行者
* @param st 语句执行者
*/
public static void closeStatement(Statement st){
if(st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
st=null;
}
}
/**
* 释放结果集
* @param rs 结果集
*/
public static void closeResultSet(ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs=null;
}
}
}
- 测试使用
public class JDBCDemo {
public static void main(String[] args) throws Exception{
//创建集合对象
List<Sort> list = new ArrayList<>();//包是util下的
try{
//使用JDBC工具类,直接获取数据库连接对象(必须在同一个工程中)
Connection con = JDBCUtils.getConnection();//JDBCUtils是工具类
//连接获取数据库SQL语句执行者对象
PreparedStatement pst = con.prepareStatement("SELECT * FROM sort");
//调用查询方法,获取结果集
ResultSet rs = pst.executeQuery();
while(rs.next()){
//获取到每个列数据,封装到Sort对象中,封装的内容直接写列名
Sort s = new Sort(rs.getInt("sid"),rs.getString("sname"),rs.getDouble("sprice"),rs.getString("sdesc"));
//封装的Sort对象,存储到集合中
list.add(s);
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.close(con, pst, rs);//关闭不用的
}
//遍历List集合,输出
for(Sort s : list){
System.out.println(s);
}
}
}
在配置文件properties中设置数据库
- properties在资源路径下:
InputStream in = JDBCUtilsConfig.class.getClassLoader().getResourceAsStream("database.properties");
Properties pro = new Properties();
pro.load(in);
String driverClass=pro.getProperty("driverClass");
String url = pro.getProperty("url");
String username = pro.getProperty("username");
String password = pro.getProperty("password");
- properties在
src
中:
在src中新建file,起名jdbc.properties
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day07
user=root
password=1234
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
// 获取指定的内容
String DRIVERCLASS = bundle.getString("driverClass");
String URL = bundle.getString("url");
String USER = bundle.getString("user");
String PASSWORD = bundle.getString("password");
练习
判断map中所有的用户名在userinfo表中是否存在存在则输出"该用户已注册",如果不存在将该用户名及对应的密码存入到userinfo表中
public class TestJdbc {
public static void main(String[] args) throws Exception {
HashMap<String, String> hm=new HashMap<>();
hm.put("liuyan", "123456");
hm.put("wangbaoqiang", "123321");
hm.put("fangbian", "123abc321");
Set<String> set=hm.keySet();
//功能1:
for(String key:set){
String value=hm.get(key);
System.out.println(key+"--"+value);
}
//功能2
Class.forName("com.mysql.jdbc.Driver");
Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/stdb","","");
PreparedStatement pst =null;
ResultSet rs =null;
for(String key:set){
String value=hm.get(key);
pst = con.prepareStatement("SELECT upassword FROM userinfo where uname='"+key+"'");
rs =pst.executeQuery() ;
if(rs.next()){
if(value.equals(rs.getString("upassword"))){
System.out.println(key+"在数据库中已存在");
}else{
pst = con.prepareStatement("INSERT into userinfo (uname,upassword ) values (?,?)");
pst.setString(1, key);
pst.setString(2, value);
pst.executeUpdate();
}
}else{
pst = con.prepareStatement("INSERT into userinfo (uname,upassword ) values (?,?)");
pst.setString(1, key);
pst.setString(2, value);
pst.executeUpdate();
}
}
JdbcDemo.close(con, pst, rs);
}
}