jdbc(Java Database Connectivity)
在C语言的Odbc基础上发展而来,是最早的ORM(Object Relation Mapping对象映射关系)工具,可以使用面向对象的Java代码
对关系型数据库进行操作。
ORM工具随着不断的发展衍生出了很多框架那么大部分框架都是在JDBC基础上发展而来,JDBC是几乎所有框架的基础,也是效率最高的ORM工具
JDBC是第一方技术,所有的接口等全部放置在java.sql包中全手动 JDBC
半自动 myBatis
全自动 Hibernate
创建数据表1)创建关系表
2)创建实体类
A:一般放置在com.etoak.po包中或者bean包中
B:尽量使用包装类封装字段,字段名对应
C:必须书写空参构造方法,酌情覆盖有参构造方法
D:酌情覆盖toString()
3)创建工厂类
A:工厂类一般放置在com.etoak.factory包中,此类链接数据库
B:加载驱动,只需要加载一次放置在静态初始化块中
C:封装一个方法返回链接
4)书写dao层
A:dao层全部放置在com.etoak.dao包中
B:接口用来设置需要被实现的方法
C:实现类实现接口中的方法
DROp table if EXISTS person; CREATE table person ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAr(10) NOT NULL, pass VARCHAr(10) NOT NULL, age INT, salary DOUBLE, birth DATE ); insert INTO person VALUES (null,'elena','12345',20,5000.00,'1990-06-03'); insert INTO person VALUES (null,'penny','123465',30,6000.00,'1993-06-13'); insert INTO person VALUES (null,'aleric','1234545',40,7000.00,'1992-06-05'); insert INTO person VALUES (null,'bonnie','1432345',20,3000.00,'1996-06-03'); insert INTO person VALUES (null,'matt','123345',24,15000.00,'1997-06-04'); insert INTO person VALUES (null,'damon','123445',23,25000.00,'1992-06-04'); insert INTO person VALUES (null,'用户名','1232245',29,5000.00,'1998-07-01');1. JavaBean实体类
如果一个Java类仅仅封装了属性,没有其他方法,则称之为POJO类或实体类,好像也叫作JavaBean,通常放置在com.xxx.po 或者 com,xxx.bean 包中
这个类中封装的属性必须对用数据库中的字段,必须覆盖空参的构造方法,酌情覆盖全参构造方法,酌情覆盖toString()方法
public class Person{
//使用包装类封装字段,必须对应表中的字段
private Integer id;
private String name;
private String pass;
private Integer age;
private Double salary;
private Date birth;
public Person() {
}
public Person(Integer id, String name, String pass, Integer age, Double salary, Date birth) {
this.id = id;
this.name = name;
this.pass = pass;
this.age = age;
this.salary = salary;
this.birth = birth;
}
//各个属性的get和set方法
//看需求是否覆盖 toString() 方法
}
2. 工厂类
一般放置在**com.xxx.factory包中** 1.加载驱动
static{
try{
Class.forName("com.mysql.jdbc.Driver");
}catch(Exception e){
e.printStackTrace();
}
}
2.获取链接
public static Connection getConn(){
try{
//2.加载链接
return DriverManager.getConnection("jdbc:mysql:///et1912","root","dream");
}catch(Exception e){
e.printStackTrace();
return null;
}
}
3.释放资源
public static void close(ResultSet rs, Statement st,Connection con){
try {
if(rs!=null)
rs.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(st!=null)
st.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(con!=null)
con.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
代码
package com.etoak.factory;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class Factory {
static{
try {
//使用反射类加载的方式加载驱动
//不同品牌的驱动不同,需要导入数据库厂商提供的jar包
Class.forName("com.mysql.jdbc.Driver");
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static Connection getCon(){
try {
return DriverManager.getConnection(
"jdbc:mysql:///et1912","root","etoaketoak");
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
public static void close(ResultSet rs, Statement st,Connection con){
try {
if(rs!=null)
rs.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(st!=null)
st.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(con!=null)
con.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
3.dao持久化层
dao(Data Access Object) 数据访问对象通常放置在**com.xxx.dao包下** 1. 接口
public interface PersonDaoIf {
//添加一个用户
public boolean addPerson(Person per);
//根据id删除一个用户
public boolean delPersonById(Integer id);
//拿取全部用户
public List queryAll();
//根据用户名查询
public boolean queryName(String name);
//根据用户名和密码查询
public Person queryPerson(String name,String pass);
//修改用户资料
public boolean updatePerson(Person per);
}
public interface PersonDaoIf2 {
//1添加用户
public boolean addPerson(Person per);
//2根据id删除
public boolean delPersonById(Integer id);
//3根据name删除
public boolean delPersonByName(String name);
//4拿取全部
public List queryAll();
//5姓名查重
public boolean queryName(String name);
//6登录查询
public Person queryPerson(String name,String pass);
//7拿取总记录数
public Integer queryCount();
//8分页查询
public List queryPage(Integer index,Integer max);
//9姓名模糊分页查询
public List queryNameLikePage(String args,Integer index,Integer max);
//10条件分页查询
public List queryLikePage(Person per,Integer index,Integer max);
//11批量删除
public boolean multiDel(String[] args);
//12修改
public boolean updatePerson(Person per);
}
2.接口实现类[Statement]
创建 PersonDaoIf 接口的实现类 PersonDaoImpl右键空白处 **(或者alt + insert)**选择 Implements Method,实现接口中的抽象方法
public class PersonDaoImpl implements PersonDaoIf{
//设置连接
Connection conn;
//设置执行器
Statement st;
//结果集
ResultSet rs;
@Override
public boolean addPerson(Person per) {
try {
//书写sql语句
String sql =
"insert into person values (null,'"+per.getName()+"','"+per.getPass()+"',"+per.getAge()+","
+per.getSalary()+",'"+new SimpleDateFormat("yyyy-MM-dd").format(per.getBirth()) +"')";
//获取连接
con = Factory.getCon();
//拿取执行器
st = con.createStatement();
//执行器根据sql语句调用方法
return st.executeUpdate(sql)==1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
//不管是否执行正常都要释放资源
Factory.close(null,st,con);
}
}
}
存在的安全隐患
当传入的是一个恒等的条件表达式的时候,语句相当于查找全表数据可以被SQL注入 3.接口实现类[PreparedStatement]
创建类PersonDaoImpl2实现PersonDaoIf2接口PreparedStatement是Statement的子类使用PreparedStatement执行器时,使用 ? 先进行站位,后填充填充日期类型时,需要用**new java.sql.Date(xxx.getDate().getTime())**
package com.etoak.dao;
import com.etoak.factory.Factory;
import com.etoak.po.Person;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class PersonDaoImpl2 implements PersonDaoIf2 {
Connection con;
Statement st;
PreparedStatement pst;
ResultSet rs;
@Override
public boolean addPerson(Person per) {
try {
String sql = "insert into person values (null,?,?,?,?,?)";
con = Factory.getCon();
//获取执行器的同时,加载带有?占位符的sql语句
pst = con.prepareStatement(sql);
//由于存在占位符所以必须填充
pst.setString(1,per.getName());
pst.setString(2,per.getPass());
pst.setInt(3,per.getAge());
pst.setDouble(4,per.getSalary());
pst.setDate(5,new java.sql.Date(per.getBirth().getTime()));
return pst.executeUpdate()==1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
//长按ctrl或者command键可以溯源
Factory.close(null,pst,con);
}
}
@Override
public boolean delPersonById(Integer id) {
try {
String sql = "delete from person where id = ?";
con = Factory.getCon();
//获取执行器的同时加载sql语句
pst = con.prepareStatement(sql);
pst.setInt(1,id);
return pst.executeUpdate()==1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
Factory.close(null,pst,con);
}
}
@Override
public boolean delPersonByName(String name) {
try {
String sql = "delete from person where name = ?";
con = Factory.getCon();
pst = con.prepareStatement(sql);
pst.setString(1,name);
return pst.executeUpdate()>=1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
Factory.close(null,pst,con);
}
}
@Override
public List queryAll() {
try {
String sql = "select * from person";
con = Factory.getCon();
pst = con.prepareStatement(sql);
rs = pst.executeQuery();
List list = new ArrayList<>();
while(rs.next()){
list.add(new Person(rs.getInt(1),
rs.getString(2),rs.getString(3),rs.getInt(4)
,rs.getDouble(5),rs.getDate(6)));
}
return list;
} catch (Exception ex) {
ex.printStackTrace();
return null;
} finally {
Factory.close(rs,pst,con);
}
}
@Override
public boolean queryName(String name) {
try {
String sql = "select * from person where name = ?";
con = Factory.getCon();
pst = con.prepareStatement(sql);
pst.setString(1,name);
return pst.executeQuery().next();
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
Factory.close(rs,pst,con);
}
}
@Override
public Person queryPerson(String name, String pass) {
try {
String sql = "select * from person where name = ? and pass = ?";
con = Factory.getCon();
pst = con.prepareStatement(sql);
pst.setString(1,name);
pst.setString(2,pass);
rs = pst.executeQuery();
if(rs.next()){
return new Person(rs.getInt(1),
rs.getString(2),rs.getString(3)
,rs.getInt(4),rs.getDouble(5),rs.getDate(6));
}
return null;
} catch (Exception ex) {
ex.printStackTrace();
return null;
} finally {
Factory.close(rs,pst,con);
}
}
@Override
public Integer queryCount() {
try {
String sql = "select count(*) from person";
con = Factory.getCon();
pst = con.prepareStatement(sql);
rs = pst.executeQuery();
rs.next();
return rs.getInt(1);
} catch (Exception ex) {
ex.printStackTrace();
return null;
} finally {
Factory.close(rs,pst,con);
}
}
@Override
public List queryPage(Integer index, Integer max) {
try {
String sql = "select * from person limit ?,?";
con = Factory.getCon();
pst = con.prepareStatement(sql);
pst.setInt(1,index);
pst.setInt(2,max);
rs = pst.executeQuery();
List list = new ArrayList<>();
while(rs.next()){
list.add(new Person(rs.getInt(1),
rs.getString(2),
rs.getString(3),
rs.getInt(4),
rs.getDouble(5),
rs.getDate(6)));
}
return list;
} catch (Exception ex) {
ex.printStackTrace();
return null;
} finally {
Factory.close(rs,pst,con);
}
}
//根据姓名模糊查询并且分页
@Override
public List queryNameLikePage(String args, Integer index, Integer max) {
try {
String sql = "select * from person where name like ? limit ?,?";
con = Factory.getCon();
pst = con.prepareStatement(sql);
//对左模糊右模糊进行拼接
pst.setString(1,"%"+args+"%");
pst.setInt(2,index);
pst.setInt(3,max);
rs = pst.executeQuery();
List list = new ArrayList<>();
while(rs.next()){
list.add(new Person(rs.getInt("id")
,rs.getString("name")
,rs.getString("pass")
,rs.getInt("age")
,rs.getDouble("salary")
,rs.getDate("birth")));
}
return list;
} catch (Exception ex) {
ex.printStackTrace();
return null;
} finally {
Factory.close(rs,pst,con);
}
}
@Override
public List queryLikePage(Person per, Integer index, Integer max) {
return null;
}
@Override
public boolean multiDel(String[] args) {
try {
String sql = "delete from person where id in (";
String sum = "";
for(int i = 0;i=1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
Factory.close(null,pst,con);
}
}
@Override
public boolean updatePerson(Person per) {
try {
String sql =
"update person set name = ?,pass = ?,age = ?,salary = ?,birth = ? where id = ?";
con = Factory.getCon();
pst = con.prepareStatement(sql);
pst.setString(1,per.getName());
pst.setString(2,per.getPass());
pst.setInt(3,per.getAge());
pst.setDouble(4,per.getSalary());
pst.setDate(5,new java.sql.Date(per.getBirth().getTime()));
pst.setInt(6,per.getId());
return pst.executeUpdate()==1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
Factory.close(null,pst,con);
}
}
}
1.书写套路注意事项
- Statement执行器
//在实现的方法中,第一步现获取连接 conn = Factory.getConn(); //第二步,获取执行器 st = conn.createStatement(); //第三步。书写Sql语句,这里可以先将SQL语句写完整之后在根据是否是字符类型的加双引号 String sql = "select * from person"; //如果没有返回结果集,则返回执行结果, //此方法返回的是受影响的行数,返回值为int return st.executeUpdate(sql) > 0; //如果有返回结果集,则第四步,获得结果集 rs = st.executeQuery(sql); //第五步,创建一个新的空集合,给定泛型确保只能存储该类型的对象 Listlist = new ArrayList<>(); //第六步 把结果集中的结果依次添加到空集合中 while(rs.next()){ list.add(new Person(rs.getInt(1),...)); } //第七步,关闭连接 Factory.closeConn(rs,st,conn);
- PreparedStatement执行器
//1获取连接 conn = Factory.getConn(); //2.书写sql语句,先使用占位符 ? 来填充 String sql = "select * from person where name like ? limit ?,?"; //3.获取执行器d的同时把sql语句传入 pst = conn.preparedStatement(sql); //4.给占位符传入相应的值,第一个参数代表是sql语句中第几个问号,第二个参数代表传入的值 //4.1 模糊查询时拼接字符串 pst.setString(1,"%" + per.getName() + "%"); //4.2传入其他参数 pst.setInt(2,per.getAge()); //4.3 Date类型,因为util.Date不能拼接sql语句,所以要转化成sql.Date pst.setDate(3,new java.sql.Date(per.getBirth().getTime())); //5.执行 pst.executeQuery();2. Date注意事项
- 执行sql语句拼接Date时
java.util.Date无法和字符串直接通过+拼接成sql语句
可以有以下两种方式
1)将java.util.Date转换为java.sql.Date,可以直接拼接
2)将java.util.Date转换为字符串
String sql = "insert into person values('"+ new SimpleDateFormat("yyyy-MM-dd").format(Date) +"')"
- 创建对象把String转换成Date类型时
Person per = new Person(new SimpleDateFormat("yyyy-MM-dd").parse("1999-01-01"));
- PreparedStatement传入Date
pst = conn.preparedStatement(sql); pst.setDate(1,new java.sql.Date(per.getBirth().getTime()));4. 测试
package com.etoak.test;
import com.etoak.factory.Factory;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class Test {
public static void main(String[] args) {
try {
//调用工厂获取连接,从而链接数据库
Connection con = Factory.getCon();
if(con==null){
System.out.println("链接失败!!");
return;
}
System.out.println("链接成功!!");
Statement st = con.createStatement();
String dml1 = "insert into person values (null,'测试1','12345',30,2000.00,'2000-03-03')";
String dql1 = "select * from person";
//int count = st.executeUpdate(dml1);
//System.out.println(count);
ResultSet rs = st.executeQuery(sql1);
while(rs.next()){
System.out.println("ID:"+rs.getInt("id")+"t姓名:"
+rs.getString("name")+"t密码:"+rs.getString("pass")
+"t年龄:"+rs.getInt("age")+"t薪资:"+rs.getDouble("salary")
+"t生日:"+rs.getDate("birth"));
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
1. 注意点
- 创建执行器
通常有两种 Statement , PreparedStatement
| 返回类型 | 执行器方法 | 解释 |
|---|---|---|
| boolean | execute() | 如果执行DML语句则返回true 如果执行DML语句,则返回false,但是依然可以执行 此方法使用较少 |
| int | executeUpdate() | 如果执行DQL语句立即报错 执行DML语句则返回受影响的记录数 |
| ResultSet | executeQuery() | 如果执行DML语句立即报错 执行DQL语句返回一个结果集ResultSet,通过解析这个结果集,可以拿取封装在里面的值 |
| int[] | executeBatch() | 执行批处理,一次执行多条SQL语句 |
| 语言 | 全称 | 包含内容 |
|---|---|---|
| SQL | Structured Query Language,结构化查询语言 | DDL、DML、DQL、DCL、TCL |
| DDL | Data Definetion Language,数据定义语言 | create(新建)、alter(修改)、drop(删除)、truncate(截断) |
| DML | Data Manipulation Language,数据操纵语言 | insert (插入)、update(修改)、delete(删除) |
| DQL | Data Query Language,数据查询语言 | select(查询) |
| DCL | Data Control Language数据控制语言 | grant(授权)、revoke(取消授权) |
| TCL | Translation Control Language事务控制语言 | commit(提交)、rollback(回滚)、savepoint(保存还原点),只会影响DML操作。 |
- 拿取有效数据
要进行解析之前,首先判断是否存在有效数据,如果没有有效数据,则不需要进行任何解析
不要根据ResultSet是否为null,来判断是否存在有效数据,因为ResultSet类似一个表格, 存在表头,永远不为null, 存在一个 boolean next() 结果集类似一个表格,
默认指针指向第一行表头,当我们调用 rs .next() 时,如果指针可以下移,返回true,如果不存在有效数据了,则指针无法下移一行,返回false所以我们可以根据.next()方法是否返回true来判断是否存在有效数据
rs = st.executeQuery(sql);
while(rs.next()){
//TODO
}
- 拿取数据
可以通过 get数据类型(列数) 或者 get数据类型(列名)来拿取
Integer id = rs.getInt(1);
String name = rs,getString("name");
2.执行器的executeBatch()方法
首先需要关闭mysql的自动提交事务,conn.setAutoCommit(false); 出错时不会提交用 addBatch(sql)方法 把要执行的SQL语句挨个放进执行器中执行方法并提交打开MySQL的自动提交
public class TestBatch {
public static void main(String[] args) {
try {
//获取连接
Connection con = Factory.getCon();
//关闭mysql的自动提交事务
con.setAutoCommit(false);
//获取执行器
Statement st = con.createStatement();
//设置要批处理得sql
//注意批处理不能执行dql语句
String dml1 = "insert into person values (null,'elena','12345',30,5000.00,'1990-03-01')";
String dml2 = "insert into person values (null,'damon','33345',20,6000.00,'1993-03-01')";
String dml3 = "insert into person values (null,'stefan','53345',17,4000.00,'1992-03-01')";
String dml4 = "delete from person where id = 1";
//将要执行的sql语句添加进缓存中
//这里每条sql语句都类似一个子弹,此处压入弹夹
st.addBatch(dml1);
st.addBatch(dml2);
st.addBatch(dml3);
st.addBatch(dml4);
//进行批处理操作
int[] count = st.executeBatch();
//提交事务
con.commit();
//恢复自动提交
con.setAutoCommit(true);
for(int i:count){
System.out.println("更改的记录数是~~~~》"+i);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
5. Statement和PreparedStatement的区别
- 相同点
两者都为接口
public interface Statement implements Wrapper
public interface PreparedStatement implements Statement PreparedStatement是Statement的子类
- 不同点
①:Statem只能执行静态语句; PreparedStatement 可以还行IN参数的sql语句,所谓IN参数是指,sql语句可以进行字段等数据的更改
并不是一个固定的语句。②PreparedStatement存在一个强大缓存区,可以对sql语句
进行预编译,在执行相同的sql语句时,PreparedStatement
将语句加载进缓存区,仅仅编译一次,当第二次执行此语句时
不需要再次进行编译,也就是说相同的sql语句执行多条
仅仅编译一次,PreparedStatement仅对改动数据进行修改
而不再进行编译;
Statement只要语句发生了改变,则必须重新进行编译③:PreparedStatement支持对sql语句使用?占位符,从而对
sql语句进行字段参数的修改,降低了开发难度,并且从根本上杜绝了
sql注入安全隐患④如果sql语句不需要多次执行,或者?过多,则效率可能较
Statement低



