文章目录
1. 快速入门
//1.导入jar包
//2.注册驱动,可省略,因为已经有java.sql.Driver写好了注册驱动
//Class.forName("com.mysql.cj.jdbc.Driver");
//3.获取数据库连接对象
Connection conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/db1?&useSSL=false&serverTimezone=Asia/Shanghai","root","lixiang1997");
//4.定义sql语句
String sql="update stu set score=66.1 where id=3";
//5.获取执行sql的对象
Statement stmt=conn.createStatement();
//6.执行sql
int count=stmt.executeUpdate(sql);
//7.处理结果
System.out.println(count);
//8.释放资源
stmt.close();
conn.close();
2. 对象详解
-
DriverManager 驱动管理对象
注册驱动:告诉程序该使用哪一个数据库驱动jar
通过查看源码发现,com.mysql.cj.jdbc.Driver类中存在静态代码块:java.sql.DriverManager.registerDriver(new Driver());
获取数据库连接
static Connection getConnection(String url,String user,String password)
url:连接路径。不同语法都有差异。 -
Connection 数据库连接对象
获取执行sql的对象
Statement creatStatement()
PreparedStatement prepareStatement(String sql)
管理事务
开启事务:void setAutoCommit(boolean autoCommit) :调用方法设置参数为false,即开启事务
提交事务:void commit()
回滚事务:void rollback() -
Statement 执行sql的对象
boolean execute(String sql)
:可以执行任意sql(不常用)
int executeUpdate(String sql)
:执行DML(增删改)语句、DDL(creat,alter,drop)语句,返回影响的行数,可以通过影响的行数判断语句执行结果
ResultSet executeQuery(String sql)
:执行DQL(查询)语句 -
ResultSet 结果集对象,封装查询结果
boolean next()
:游标向下移动一行,并判断当前行是否为最后一行,是则返回false,不是则返回true,最开始游标在字段名那行,但while循环会先执行再判断
Xxx getXxx(参数)
:Xxx为数据类型,获取并返回相应数据类型
参数int:表示列的编号从1开始
参数String:表示列名称(字段名)
使用前应该先判断有没有数据 -
PreparedStatement 执行sql对象
sql注入问题
用户随便输用户名,再密码输入a’ or ‘a’='a,形成恒等式,会导致错误输出,安全性问题
解决sql注入问题
预编译sql:参数使用?
作为占位符替代
定义sql时参数使用?
作为占位符。如select*from 表 where 字段=? and 字段=?;
获取执行sql对象Connection.PreparedStatement(String sql)
给?
赋值,方法setXxx(?编号从1开始,?的值)
执行sql,接受返回结果,不需要参数
注意:后期都会使用PreparedStatement 执行sql对象,来完成增删改查,效率高
3. 基于Statement的操作
3.1 insert
public static void main(String[] args) {
Statement stmt=null;
Connection conn=null;
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.定义sql
String sql="insert into stu values(4,'小黄',21,22.1,'1998-2-3',null,'男')";
//3.获取connection对象
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/db1?&useSSL=false&serverTimezone=Asia/Shanghai","root","lixiang1997");
//4.获取执行sql对象Statement
stmt= conn.createStatement();
//5.执行sql
int count=stmt.executeUpdate(sql);
//6.处理结果
if (count>0){
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//stmt.close();错误,因为会有空指针异常
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.2 update
public static void main(String[] args) {
Connection conn=null;
Statement stmt=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
try {
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/db1?&useSSL=false&serverTimezone=Asia/Shanghai","root","lixiang1997");
stmt=conn.createStatement();
String sql="update stu set score=65.7 where id=4";
int count=stmt.executeUpdate(sql);
if (count>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
} catch (SQLException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.3 delete
public static void main(String[] args) {
Connection conn=null;
Statement stmt=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
try {
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/db1?&useSSL=false&serverTimezone=Asia/Shanghai","root","lixiang1997");
stmt=conn.createStatement();
String sql="delete from stu where id=4";
int count=stmt.executeUpdate(sql);
if (count>0){
System.out.println("执行成功");
}else {
System.out.println("执行失败");
}
} catch (SQLException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.4 create table
public static void main(String[] args) {
Connection conn=null;
Statement stmt=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
try {
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/db1?&useSSL=false&serverTimezone=Asia/Shanghai","root","lixiang1997");
stmt=conn.createStatement();
String sql="create table stu3(id int,name varchar(10))";
stmt.executeUpdate(sql);
} catch (SQLException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.5 select
public static void main(String[] args) {
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
try {
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/db1?&useSSL=false&serverTimezone=Asia/Shanghai","root","lixiang1997");
stmt=conn.createStatement();
String sql="select * from stu";
rs=stmt.executeQuery(sql);
while (rs.next()==true /*游标下移一行,同时给出是否到末尾的判断*/){
//获取数据
int id=rs.getInt(1);
String name =rs.getString("name");
double score=rs.getDouble(4);
System.out.println(id+" "+name+" "+score);
}
} catch (SQLException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
if (rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
也可以用ResultSet获取的数据实例化一个对象 再放到ArrayList中
4. JDBCUtils
抽取jdbc工具类以简化书写:JDBCUtils
注册驱动
抽取一个方法获取连接对象
抽取一个方法释放资源
public class JDBCUtils {
/*获取连接,返回连接对象*/
//不想传参还要保证工具类的通用性,解决方案:配置文件
//希望文件的读取只需要一次就可拿到这些值。使用静态代码块
private static String url;
private static String user;
private static String password;
private static String driver;
static {
//读取资源文件获取值
try {//1.创建Properties集合类
Properties pro=new Properties();
//获取src下的文件路径的方式--》ClassLoader类加载器
ClassLoader classLoader=JDBCUtils.class.getClassLoader();
URL res=classLoader.getResource("jdbc.properties");
String path=res.getPath();///C:/Users/ROG/Desktop/myjavaij/out/production/myjavaij/jdbc.properties为绝对路径
//System.out.println(path);
//2.加载文件
//pro.load(new FileReader("src/jdbc.properties"));
pro.load(new FileReader(path));
//3.获取值
url=pro.getProperty("jdbc.url");
user=pro.getProperty("jdbc.user");
password=pro.getProperty("jdbc.password");
driver=pro.getProperty("jdbc.driver");
//4.注册驱动
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static Connection getConnections() throws SQLException{
return DriverManager.getConnection(url,user,password);
}
/*一定要分开写,释放资源*/
public static void close(Statement stmt,Connection conn){
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Statement stmt, Connection conn, ResultSet rs){
if (rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
下面模仿一个登录操作
public class test4 {
private int id;
private String username;
private String password;
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public int getId() {
return id;
}
public test4(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public static List<test4> load(){
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
List<test4> list=new ArrayList<test4>();
try {
conn= JDBCUtils.getConnections();
stmt=conn.createStatement();
String sql="select * from user";
rs=stmt.executeQuery(sql);
while (rs.next()){
list.add(new test4(rs.getInt("id"),rs.getString("username"),rs.getString("password")));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(stmt,conn,rs);
}
return list;
}
public static boolean login(List<test4> list){
Scanner scanner=new Scanner(System.in);
System.out.println("请输入您的用户名:");
String username=scanner.next();
System.out.println("请输入您的密码:");
String password=scanner.next();
for (test4 t4:list) {
if (t4.getPassword().equals(password)&&t4.getUsername().equals(username)){
System.out.println("登入成功,"+t4.getId()+"号用户欢迎您!");
scanner.close();
return true;
}
}
System.out.println("用户名或密码错误!");
return false;
}
//简单写法!!
public static boolean login2(String username,String password) {
if (username==null&&password==null){
return false;
}
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try {
conn= JDBCUtils.getConnections();
stmt=conn.createStatement();
String sql="select * from user where username='"+username+"' and password='"+password+"'";
rs=stmt.executeQuery(sql);
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(stmt,conn,rs);
}
return false;
}
public static void main(String[] args) {
/*
List list=load();
boolean flag=false;
//System.out.println(list);
while (!flag){
flag=login(list);
}
*/
Scanner scanner=new Scanner(System.in);
String usrname=scanner.next();
String password=scanner.next();
System.out.println(login2(usrname,password));
}
}
5. PreparedStatement
上面的登录操作是有问题的,会产生sql注入安全隐患,PreparedStatement
可以用占位符?
替代输入的信息,防止sql注入,下面重写登录操作
public class test5 {
//登入,用PreparedStatement预编译sql实现,避免了安全性问题
public static boolean login(String username,String password) {
if (username==null&&password==null){
return false;
}
Connection conn=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
try {
conn= JDBCUtils.getConnections();
String sql="select * from user where username=? and password=?";
pstmt=conn.prepareStatement(sql);
//给?赋值
pstmt.setString(1,username);
pstmt.setString(2,password);
rs=pstmt.executeQuery();
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(pstmt,conn,rs);
}
return false;
}
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
String usrname=scanner.next();
String password=scanner.next();
System.out.println(login(usrname,password));
}
}
6. jdbc控制事务:包含多个步骤的操作只允许一起成功失败
用Connection对象来管理事务
开启事务:SetAutoCommit(boolean autoCommit)调用该方法设置参数为false,开启事务 在所有sql语句执行前开启
提交事务:commit() 在所有sql都执行完时提交
回滚事务:rollback() 在catch中回滚(catch抓到异常说明发生了错误)
下面模仿一个银行转账案例
public class test6 {
public static void main(String[] args) {
int moveMoney=500;
Connection conn=null;
PreparedStatement pstmt=null;
try {
conn= JDBCUtils.getConnections();
String sql1="update sal set money=sal.money+? where id=?";
conn.setAutoCommit(false);
pstmt=conn.prepareStatement(sql1);
//id为1的用户到账500
pstmt.setInt(1,moveMoney);
pstmt.setInt(2,1);
int count=pstmt.executeUpdate();
//手动制造异常 int i=3/0;
//id为2的用户账户金额-500
pstmt.setInt(1,-moveMoney);
pstmt.setInt(2,2);
int count2=pstmt.executeUpdate();
conn.commit();
System.out.println("转账成功!");
} catch (Exception e) {
//在catch中回滚事务
try {
if (conn!=null){
conn.rollback();
}
System.out.println("转账失败!");
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(pstmt,conn);
}
}
}
7. 连接池技术
连接池其实就是一个容器(集合),存放数据库连接的容器。当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
优点
1.节约资源
2.用户访问高效
方法:
标准接口:DataSource
, java.sql
包下的,我们不会去实现,一般由数据库厂商来实现
获取连接:getConnection()
归还连接:Connection.close()。如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接
7.1 c3p0连接池
1.导入jar包(两个) c3p0以及依赖
2.定义配置文件 c3p0.properties
或者 c3p0-config.xml
,直接将文件放在src目录下即可
<c3p0-config>
<default-config>
<!--默认是mysql数据库 -->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db2?serverTimezone=Asia/Shanghai</property>
<property name="user">root</property>
<property name="password">lixiang1997</property>
<!-- 连接池参数 -->
<!--初始化申请的连接数量-->
<property name="initialPoolSize">5</property>
<!--最大的连接数量-->
<property name="maxPoolSize">10</property>
<!--超时时间-->
<property name="checkoutTimeout">3000</property>
</default-config>
</c3p0-config>
测试连接
public static void main(String[] args) throws SQLException {
//1.创建数据库连接池对象,不传参数使用默认配置,可以传对应name的值,实现指定连接的配置
DataSource ds = new ComboPooledDataSource();
//获取10(最大)个连接对象,改成11就报错了
for (int i = 0; i <11 ; i++) {
Connection conn=ds.getConnection();
System.out.println((i+1)+":"+conn);
//如何实现10个以上的获取连接,可以归还用完的连接
if (i==5){
conn.close();//归还连接到连接池中
}
}
}
7.2 Druid连接池
Druid:数据库连接池技术,由阿里巴巴提供的
1.导入jar包 druid-1.0.9.jar
2.定义配置文件: 是properties形式的,可以叫任何名称,可以放在任意目录下,需手动加载
3.加载配置文件:properties
4.获取数据库连接池对象:通过工厂类来获取 DruidDataSourceFactory
5.获取连接 getConnection
druid.properties
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/db2?serverTimezone=Asia/Shanghai
username=root
password=lixiang1997
#初始化连接数量
initialSize=5
#最大连接数
maxActive=10
#最大等待时间
maxWait=3000
maxIdle=8
minIdle=3
测试连接
//1.导入jar包
//2.定义配置文件
//3.加载配置文件
Properties pro=new Properties();
InputStream is=druid.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//4.获取连接池对象
DataSource ds=DruidDataSourceFactory.createDataSource(pro);
//5.获取连接
Connection conn=ds.getConnection();
System.out.println(conn);
定义工具类JDBCUtils(Druid)
1.提供静态代码块加载配置文件,初始化连接池对象
2.获取连接方法:通过数据库连接池获取连接
3.释放资源
4.获取连接池的方法
public class JDBCUtilsDruid {
//1.定义成员变量 DataSource
private static DataSource ds;
static {
try {
//1.加载配置文件
Properties pro=new Properties();
pro.load(JDBCUtilsDruid.class.getClassLoader().getResourceAsStream("druid.properties"));
//2.获取DataSource
ds= DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//释放资源
public static void close(Statement stmt,Connection conn){
close(stmt,conn,null);
}
public static void close(Statement stmt, Connection conn, ResultSet rs){
if (rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();//归还连接
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//获取数据元
public static DataSource getDataSource(){
return ds;
}
}
使用新的工具类,完成添加的操作
public static void main(String[] args) {
Connection conn=null;
PreparedStatement pstmt=null;
try {
//1.获取连接
conn = JDBCUtilsDruid.getConnection();
//2.定义sql
String sql="insert into sal value(null,'小红',2000)";
//3.生成执行sql的对象
pstmt=conn.prepareStatement(sql);
//4.执行sql
int count=pstmt.executeUpdate();
System.out.println(count);
} catch (SQLException e) {
e.printStackTrace();
}finally {
//5.释放资源
JDBCUtilsDruid.close(pstmt,conn);
}
}
8. Spring JDBC
Spring JDBC
是Spring框架提供的JDBC简单封装。提供了JDBCTemplate对象简化JDBC的开发
1.导入jar包
2.创建JDBCTemplate对象。依赖于数据源DataSource: JdbcTemplate template=new JdbcTemplate(ds);
3.调用JDBCTemplate的方法完成CRUD的操作
连接测试
public static void main(String[] args) {
int count;
//1.导入jar包,也要导入jdbc基本连接
//2.创建JdbcTemplate对象
JdbcTemplate template=new JdbcTemplate(JDBCUtilsDruid.getDataSource());
//3.调用方法
String sql="update sal set money=5000 where id=?";//更新一条记录
String sql2="insert into sal value(null,?,?)";//添加一条记录
String sql3="delete from sal where id=?";//删除一条记录
count=template.update(sql3,4);//sql后的每个参数对应每个问号的值,这个方法返回影响的行数
//count=template.update(sql2,"小微",3000);//sql后的每个参数对应每个问号的值,这个方法返回影响的行数
//count=template.update(sql1,3);//sql后的每个参数对应每个问号的值,这个方法返回影响的行数
System.out.println(count);
}
调用JDBCTemplate的方法完成CRUD的操作
update()
:执行增删改语句
queryForMap()
:查询结果集将结果集封装为map集合,将字段作为键,对应的值作为值,封装进键值对(查询的结果集长度只能为1)
queryForList()
:查询结果集将结果集封装为list集合,把queryForMap的键值对装进线性表中
query()
:查询结果将结果封装为JavaBean对象,一般使用BeanPropertyRowMapper实现类,可以完成数据到JavaBean的自动封装:
new BeanPropertyRowMapper<类>(类.class)
queryForObject()
:查询结果将结果封装为对象(基本对象),一般用来执行聚合函数(查找记录条数)
测试实体类
public class sal {
private Integer id;
private String name;
private Integer money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getMoney() {
return money;
}
public void setMoney(Integer money) {
this.money = money;
}
public sal(Integer id, String name, Integer money) {
this.id = id;
this.name = name;
this.money = money;
}
public sal() {
this.id = null;
this.name = null;
this.money = null;
}
@Override
public String toString() {
return "sal{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
测试类
public class test2 {
private JdbcTemplate template=new JdbcTemplate(JDBCUtilsDruid.getDataSource());
//查询id为1的记录将其封装为map
@Test
public void test1(){
String sql="select * from sal where id=?";//查询
//queryForMap方法将字段作为键,对应的值作为值,封装进键值对(查询的结果集长度只能为1)
Map<String,Object> map=template.queryForMap(sql,1);
System.out.println(map);
}
//查询所有记录,将其封装为list
@Test
public void test2(){
String sql="select * from sal";//查询
//queryForList方法把map的键值对装进线性表中
List<Map<String,Object>> list=template.queryForList(sql);
for (Map<String,Object> map:list) {
System.out.println(map);
}
}
//查询所有记录,将其封装为emp对象的list集合
@Test
public void test3(){
String sql="select * from sal";//查询
//实现RowMapper泛型接口中的mapRow方法,这里用到了匿名内部类
List<sal> list=template.query(sql,new RowMapper<sal>(){
@Override
public sal mapRow(ResultSet rs, int i) throws SQLException {
sal sal=new sal(rs.getInt("id"),rs.getString("name"),rs.getInt("money"));
return sal;
}
});
for (sal sal:list) {
System.out.println(sal);
}
}
//查询所有记录,将其封装为emp对象的list集合
@Test
public void test3_2(){
String sql="select * from sal";//查询
//这里用封装好的类实现,更加简单了
List<sal> list=template.query(sql,new BeanPropertyRowMapper<sal>(sal.class));
for (sal sal:list) {
System.out.println(sal);
}
}
//查询总记录数
@Test
public void test4(){
String sql="select count(id) from sal";//查询
//queryForObject方法的第二个参数是返回值的类型
long total=template.queryForObject(sql,long.class);
System.out.println(total);
}
}