JDBC (Java DataBase Connection) 指通过JAVA访问数据库。
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class TestJDBC {
public static void main(String[] args) {
//初始化驱动类com.mysql.jdbc.Driver
try { Class.forName("com.mysql.jdbc.Driver").newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//建立与数据库的Connection连接(前提MySQL中已经创建了数据库)
这里需要提供:
数据库所处于的ip:127.0.0.1 (本机)
数据库的端口号: 3306 (mysql专用端口号)
数据库名称 how2java
编码方式 UTF-8
账号 root
密码 admin
try (
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8",
"root", "admin");
//Statement是用于执行SQL语句的(使用java.sql.Statement),比如增加,删除
Statement s = c.createStatement();
)
{
String sql = "insert into hero values(null," + "'提莫'" + "," + 313.0f + "," + 50 + ")";
s.execute(sql);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
使用try-with-resource的方式自动关闭连接,因为Connection和Statement都实现了AutoCloseable接口 。
CRUD是最常见的数据库操作,即增删改查
C 增加(Create)
R 读取查询(Retrieve)
U 更新(Update)
D 删除(Delete)
在JDBC中增加,删除,修改的操作都很类似,只是传递不同的SQL语句就行了。
删除和增加很类似,只不过是执行的SQL语句不一样罢了 String sql = "delete from hero where id = 5"
;
修改也一样,执行另一条SQL语句就可以了 String sql =
"update hero set name = 'name 5' where id = 3"
;
查询:executeQuery 执行SQL查询语句
注意: 在取第二列的数据的时候,用的是rs.get(2) ,而不是get(1). 这个是整个Java自带的api里唯二的地方,使用基1的,即2就代表第二个。
另一个地方是在PreparedStatement这里
String sql =
"select * from hero"
;
// 执行查询语句,并把结果集返回给ResultSet
ResultSet rs = s.executeQuery(sql);
while
(rs.next()) {
int
id = rs.getInt(
"id"
);
// 可以使用字段名
String name = rs.getString(
2
);
// 也可以使用字段的顺序
float
hp = rs.getFloat(
"hp"
);
int
damage = rs.getInt(
4
);
System.out.printf(
"%d\t%s\t%f\t%d%n"
, id, name, hp, damage);
}
// 不一定要在这里关闭ReultSet,因为Statement关闭的时候,会自动关闭ResultSet
// rs.close();
和Statement一样,预编译
PreparedStatement也是用来执行sql语句的,与创建Statement不同的是,需要根据sql语句创建PreparedStatement
除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接。
和ResultSet,都是基1的。
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestJDBC {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
} catch (InstantiationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IllegalAccessException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//先创建sql语句
String sql = "insert into hero values(null,?,?,?)";
try(Connection c= DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/cxyjava?characterEncoding=UTF-8",
"root", "admin");
// 根据sql语句创建PreparedStatement
PreparedStatement ps =c.prepareStatement(sql);
)
{
// 设置参数
ps.setString(1, "提莫");
ps.setFloat(2, 313.0f);
ps.setInt(3, 50);
ps.execute();
}catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
PreparedStatement的优点1-参数设置
PreparedStatement的优点2-性能表现
PreparedStatement有预编译机制,性能比Statement更快 。
Statement执行10次,需要10次把SQL语句传输到数据库端,
数据库要对每一次来的SQL语句进行编译处理
PreparedStatement 执行10次,只需要1次把SQL语句传输到数据库端,数据库对带?的SQL进行预编译
每次执行,只需要传输参数到数据库端
1. 网络传输量比Statement更小
2. 数据库不需要再进行编译,响应更快
PreparedStatement的优点3-防止SQL注入式攻击
假设name是用户提交来的数据
String name = "'盖伦' OR 1=1";
使用Statement就需要进行字符串拼接
拼接出来的语句是:
select * from hero where name = '盖伦' OR 1=1
因为有OR 1=1,这是恒成立的
那么就会把所有的英雄都查出来,而不只是盖伦,并且暴露所有信息
如果Hero表里的数据时海量的,比如几百万条,把这个表里的数据全部查出来
会让数据库负载变高,CPU100%,内存消耗光,响应变得极其缓慢
而PreparedStatement使用的是参数设置,就不会有这个问题
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestJDBC {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String sql = "select * from hero where name = ?";
try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root", "admin");
Statement s = c.createStatement();
PreparedStatement ps = c.prepareStatement(sql);
) {
// 假设name是用户提交来的数据
String name = "'盖伦' OR 1=1";
String sql0 = "select * from hero where name = " + name;
// 拼接出来的SQL语句就是
// select * from hero where name = '盖伦' OR 1=1
// 因为有OR 1=1,所以恒成立
// 那么就会把所有的英雄都查出来,而不只是盖伦
// 如果Hero表里的数据时海量的,比如几百万条,把这个表里的数据全部查出来
// 会让数据库负载变高,CPU100%,内存消耗光,响应变得极其缓慢
System.out.println(sql0);
ResultSet rs0 = s.executeQuery(sql0);
while (rs0.next()) {
String heroName = rs0.getString("name");
System.out.println(heroName);
}
s.execute(sql0);
// 使用预编译Statement就可以杜绝SQL注入
ps.setString(1, name);
ResultSet rs = ps.executeQuery();
// 查不出数据出来
while (rs.next()) {
String heroName = rs.getString("name");
System.out.println(heroName);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
1.Statement、PreparedStatement和CallableStatement都是接口(interface)。
2.Statement继承自Wrapper、PreparedStatement继承自Statement、CallableStatement继承自PreparedStatement。
3.
Statement接口提供了执行语句和获取结果的基本方法;
PreparedStatement接口添加了处理 IN 参数的方法;
CallableStatement接口添加了处理 OUT 参数的方法。
4.
a.Statement:
普通的不带参的查询SQL;支持批量更新,批量删除;
b.PreparedStatement:
可变参数的SQL,编译一次,执行多次,效率高;
安全性好,有效防止Sql注入等问题;
支持批量更新,批量删除;
c.CallableStatement:
继承自PreparedStatement,支持带参数的SQL操作;
支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持;
execute与executeUpdate
1.相同点
都可以执行增加,删除,修改
2.不同点
不同1:
execute可以执行查询语句
然后通过getResultSet,把结果集取出来
executeUpdate不能执行查询语句
不同2:
execute返回boolean类型,true表示执行的是查询语句,false表示执行的是insert,delete,update等等
executeUpdate返回的是int,表示有多少条数据受到了影响
关于
方法executeQuery
用于产生单个结果集的语句,例如 SELECT 语句。 被使用最多的执行 SQL 语句的方法是 executeQuery。这个方法被用来执行 SELECT 语句,它几乎是使用最多的 SQL 语句。
获取自增长id
在Statement通过execute或者executeUpdate执行完插入语句后,MySQL会为新插入的数据分配一个自增长id,(前提是这个表的id设置为了自增长,在Mysql创建表的时候,AUTO_INCREMENT就表示自增长)
CREATE TABLE hero (
id int(11) AUTO_INCREMENT,
...
}
数据库知道自增长是多少,但是无论是execute还是executeUpdate都不会返回这个自增长id是多少。需要通过Statement的getGeneratedKeys获取该id
注: 第20行的代码,后面加了个Statement.RETURN_GENERATED_KEYS参数,以确保会返回自增长ID。 通常情况下不需要加这个,有的时候需要加,所以先加上,保险一些
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestJDBC {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String sql = "insert into hero values(null,?,?,?)";
try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root", "admin");
PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
) {
ps.setString(1, "盖伦");
ps.setFloat(2, 616);
ps.setInt(3, 100);
// 执行插入语句
ps.execute();
// 在执行完插入语句后,MySQL会为新插入的数据分配一个自增长id
// JDBC通过getGeneratedKeys获取该id
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
int id = rs.getInt(1);
System.out.println(id);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
获取表的元数据
元数据概念:
和数据库服务器相关的数据,比如数据库版本,有哪些表,表有哪些字段,字段类型是什么等等。
package jdbc;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestJDBC {
public static void main(String[] args) throws Exception {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/cxyjava?characterEncoding=UTF-8","root", "admin");) {
// 查看数据库层面的元数据
// 即数据库服务器版本,驱动版本,都有哪些数据库等等
DatabaseMetaData dbmd = c.getMetaData();
// 获取数据库服务器产品名称
System.out.println("数据库产品名称:\t"+dbmd.getDatabaseProductName());
// 获取数据库服务器产品版本号
System.out.println("数据库产品版本:\t"+dbmd.getDatabaseProductVersion());
// 获取数据库服务器用作类别和表名之间的分隔符 如test.user
System.out.println("数据库和表分隔符:\t"+dbmd.getCatalogSeparator());
// 获取驱动版本
System.out.println("驱动版本:\t"+dbmd.getDriverVersion());
System.out.println("可用的数据库列表:");
// 获取数据库名称
ResultSet rs = dbmd.getCatalogs();
while (rs.next()) {
System.out.println("数据库名称:\t"+rs.getString(1));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
使用事务
在Mysql中,只有当表的类型是INNODB的时候,才支持事务,所以需要把表的类型设置为INNODB,否则无法观察到事务.
在事务中的多个操作,要么都成功,要么都失败
通过 c.setAutoCommit(false);关闭自动提交
使用 c.commit();进行手动提交
在22行-35行之间的数据库操作,就处于同一个事务当中,要么都成功,要么都失败
所以,虽然第一条SQL语句是可以执行的,但是第二条SQL语句有错误,其结果就是两条SQL语句都没有被提交。 除非两条SQL语句都是正确的。
ORM
即Object Relationship Database Mapping
对象和关系数据库的映射
简单说,一个对象,对应数据库里的一条记录
根据ORM的思想,设计其他几个常见的ORM方法:
1.把一个Hero对象插入到数据库中
public static void add(Hero h)
2.把这个Hero对象对应的数据删除掉
public static void delete(Hero h)
3.更新这条Hero对象
public static void update(Hero h)
4.把所有的Hero数据查询出来,转换为Hero对象后,放在一个集合中返回
public static List<Hero> list();
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import charactor.Hero;
public class TestJDBC2 {
//提供方法get(int id)
//返回一个Hero对象
public static Hero get(int id) {
Hero hero = null;
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try(Connection c =DriverManager.getConnection
("jdbc:mysql://127.0.0.1:3306/cxyjava?characterEncoding=UTF-8","root", "admin");
Statement s =c.createStatement();)
{
String sql = "select * from hero where id="+id;
ResultSet rs= s.executeQuery(sql);
// 因为id是唯一的,ResultSet最多只能有一条记录
// 所以使用if代替while
if(rs.next()) {
hero = new Hero();
String name = rs.getString(2);
Float hp = rs.getFloat(3);
int damage =rs.getInt(4);
hero.name= name;
hero.hp = hp;
hero.damage = damage;
hero.id = id;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return hero;
}
//把一个Hero对象插入到数据库中
public static void add(Hero h) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//预处理
String sql = "insert into hero values(null,?,?,?)";
try(Connection c =DriverManager.getConnection
("jdbc:mysql://127.0.0.1:3306/cxyjava?characterEncoding=UTF-8","root", "admin");
PreparedStatement ps = c.prepareStatement(sql);)
{
ps.setString(1, h.name);
ps.setFloat(2, h.hp);
ps.setInt(3, h.damage);
ps.execute();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//把这个Hero对象对应的数据删除掉
public static void delete(Hero h) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//预处理
String sql = "delete from hero where id =?";
try(Connection c =DriverManager.getConnection
("jdbc:mysql://127.0.0.1:3306/cxyjava?characterEncoding=UTF-8","root", "admin");
PreparedStatement ps = c.prepareStatement(sql);)
{
ps.setInt(1, h.id);
ps.execute();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//更新这条Hero对象
public static void update(Hero h) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String sql = "update hero set name = ?,hp =?,damage = ? where id =?";
try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/cxyjava?characterEncoding=UTF-8",
"root", "admin");
PreparedStatement ps = c.prepareStatement(sql);)
{ // 设置参数
ps.setString(1, h.name);
ps.setFloat(2, h.hp);
ps.setInt(3, h.damage);
ps.setInt(4, h.id);
ps.execute();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//把所有的Hero数据查询出来,转换为Hero对象后,放在一个集合中返回
public static List<Hero> list(){
List <Hero>heros = new ArrayList<>();
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try (Connection c = DriverManager.getConnection
("jdbc:mysql://127.0.0.1:3306/cxyjava?characterEncoding=UTF-8","root", "admin");
Statement s = c.createStatement();)
{
String sql ="select *from hero";
ResultSet rs = s.executeQuery(sql);
while(rs.next()) {
Hero h = new Hero();
int id = rs.getInt(1);
String name = rs.getString(2);
float hp = rs.getFloat("hp");
int damage = rs.getInt(4);
h.name = name;
h.hp = hp;
h.damage = damage;
h.id = id;
heros.add(h);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return heros;
}
public static void main(String[] args) {
/*
* list()表示返回数据库中所有的Hero记录
List<Hero> hs =list();
第一次调用的时候,hs引用就指向了这个装有 全部Hero对象的集合
接下来,做了一次增加add(),那么hs引用所指向的集合,就不再是数据库中全部的Hero对象集合了
所以需要重新执行一遍hs =list(); 使得hs重新指向一个有全部Hero对象的集合
*/
List<Hero> hs =list();
System.out.println("数据库中总共有" + hs.size() + " 条数据");
Hero h = new Hero();
h.name = "新的英雄";
System.out.println("新加一条数据");
add(h);
hs =list();
System.out.println("数据库中总共有" + hs.size() + " 条数据");
System.out.println("取出id=2的数据,它的name是:");
h = get(2);
System.out.println(h.name);
System.out.println("把名字改为 国安,并且更新到数据库");
h.name="国安";
update(h);
System.out.println("取出id=2的数据,它的name是:");
h = get(2);
System.out.println(h.name);
System.out.println("删除id=2的数据");
delete(h);
hs =list();
System.out.println("数据库中总共有" + hs.size() + " 条数据");
}
}
DAO接口
DAO=DataAccess Object
数据库访问对象
实际上就是运用了ORM中的思路,把数据库相关的操作都封装在这个类里面,其他地方看不到JDBC的代码