猿进化系列12——一文快速学会数据库访问

195 阅读10分钟

看完上一个章节,相信你已经掌握了MYSQL数据库的基本操作,以及SQL的基本写法,可是你只会用图形化工具编写和执行SQL,而在实际的程序开发中,你是需要用程序来操作数据库的,今天我们就来学习下JAVA访问数据库的姿势。

猿进化是一个原创系列文章,帮助你从一只小白快速进化为一个猿人,更多精彩内容,敬请大家关注公主号猿人工厂,点击猿人养成获取

之前我们学习了数据库的基本操作,JAVA的基本语法和一些常用的API。学习web开发,需要从数据库里动态的获取数据,需要现在我们来学习使用JAVA相关的知识来访问数据库。



JAVA通过什么来访问数据库呢?常规套路是使用JDBC来访问。JDBC是JAVAEE定义的数据库访问规范,它定义了数据库访问接口,具体的实现由各个数据库厂商去完成。JDBC仅仅是定义了接口,但是要操作数据库,还需要实现类去完成,也就是数据库驱动去完成。每个厂商都会去完成各自的数据库驱动实现,比如我们使用的MYSQL数据库,它的数据库厂商,也就是oracle,会去提供数据库驱动。使用JAVA操作数据库,我们只要会调用JDBC的方法就可以了。

看到没?这就是规范的发展套路,你不实现我就不支持你访问,你要卖数据库,要想有人用你的数据库你必须要有驱动,要不然没人用。ODBC为啥渐渐的被人忘记,因为大家支持了SUN,不过对于猿人来说也是有好处的:

1.咱们只用关心接口就好,屏蔽了个厂商之间数据库通信的细节

2.面向接口编程(想想面向对象思想那一章,用接口的好处了吧,多态呀多态无处不在),编写一套代码,用较小的修改(是不可能滴,SQL方言不一样哒)就能访问其他支持JDBC的数据库。

使用JDBC访问数据库的步骤如下:

1. 引入数据库驱动相关的jar包

在pom.xml中增加

<!--mysql驱动-->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.26</version>

<scope>compile</scope>

</dependency>

(maven自动帮我们下载)

2. 注册驱动

3. 创建数据库连接(Connection)

4. 定义sql

5. 创建执行sql语句的对象 PreparedStatement

6.设置PreparedStatement的参数

7. 执行sql,接受返回结果

8. 处理数据库返回结果 ResultSet

9. 释放资源


根据ID查询学生记录:

public class JDBCCRUDDemo {

private static void queryStudentById(Long id) throws SQLException{
Connection connection = null;
ResultSet rs = null;
PreparedStatement ps=null;
try{
//1)注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2) 得到连接对象
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test01","root","123456");
//sql无需区分大小写
String sql="select * from student where ID = ?";
//3) 得到语句对象
ps = connection.prepareStatement(sql);
//4)设置参数,parameterIndex 注意:index从1开始算起 1代表第一个参数,即第一个问号
ps.setLong(1,id);
//5) 执行SQL语句得到结果集ResultSet对象
rs = ps.executeQuery();
//6) 循环遍历取出每一条记录
while(rs.next()) {
Long dbId = rs.getLong("id");
String name = rs.getString("name");
int sex = rs.getInt("sex");
Date birthday = rs.getDate("admission_date");
String remark = rs.getString("remark");
//7) 输出的控制台上
System.out.println("编号:" + dbId + ", 姓名:" + name + ", 性别:" + sex + ", 入学日期:" + birthday+",备注:"+remark);
}
}catch(Exception e){
e.printStackTrace();
}
finally{
//8) 释放资源 一定要牢记
if(null!=rs){
rs.close();
}
if(null!=ps){
ps.close();
}
if(null!=connection){
connection.close();
}

}

}

public static void main(String args[]){

try {
queryStudentById(2L);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

新增学生记录:

private static void addStudent(String name,int sex,Date admissionDate,String remark ) throws SQLException{
Connection connection = null;
int rows = 0;
PreparedStatement ps=null;
try{
//1)注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2) 得到连接对象
connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/test01?characterEncoding=utf8","root","123456");
//sql无需区分大小写
String sql="insert into student(name,sex,admission_date,remark) values(?,?,?,?)";
//3) 得到语句对象
ps = connection.prepareStatement(sql);
//4)设置参数,parameterIndex 注意:index从1开始算起 1代表第一个参数,即第一个问号
ps.setString(1,name);
ps.setInt(2,sex);

ps.setDate(3,new java.sql.Date(admissionDate.getTime()));
ps.setString(4,remark);
//5) 执行SQL语句得到结果集ResultSet对象
rows = ps.executeUpdate();
//6) 循环遍历取出每一条记录
//7) 输出的控制台上
System.out.println("插入记录条数:" +rows);
}catch(Exception e){
e.printStackTrace();
}
finally{
//8) 释放资源 一定要牢记
if(null!=ps){
ps.close();
}
if(null!=connection){
connection.close();
}

}

}

按ID修改学生记录:

private static void updateStudent(String name,int sex,Date admissionDate,String remark,Long id ) throws SQLException{
Connection connection = null;
int rows = 0;
PreparedStatement ps=null;
try{
//1)注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2) 得到连接对象
connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/test01?characterEncoding=utf8","root","123456");
//sql无需区分大小写
String sql="update student set name=?, sex=? ,admission_date=? ,remark =? where id=?";
//3) 得到语句对象
ps = connection.prepareStatement(sql);
//4)设置参数,parameterIndex 注意:index从1开始算起 1代表第一个参数,即第一个问号
ps.setString(1,name);
ps.setInt(2,sex);

ps.setDate(3,new java.sql.Date(admissionDate.getTime()));
ps.setString(4,remark);
ps.setLong(5, id);
//5) 执行SQL语句得到结果集ResultSet对象
rows = ps.executeUpdate();
//6) 循环遍历取出每一条记录
//7) 输出的控制台上
System.out.println("修改记录条数:" +rows);
}catch(Exception e){
e.printStackTrace();
}
finally{
//8) 释放资源 一定要牢记
if(null!=ps){
ps.close();
}
if(null!=connection){
connection.close();
}

}

}

按ID删除学生记录:

private static void deleteStudent(Long id ) throws SQLException{
Connection connection = null;
int rows = 0;
PreparedStatement ps=null;
try{
//1)注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2) 得到连接对象
connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/test01?characterEncoding=utf8","root","123456");
//sql无需区分大小写
String sql="delete from student where id=?";
//3) 得到语句对象
ps = connection.prepareStatement(sql);
//4)设置参数,parameterIndex 注意:index从1开始算起 1代表第一个参数,即第一个问号

ps.setLong(1, id);
//5) 执行SQL语句得到结果集ResultSet对象
rows = ps.executeUpdate();
//6) 循环遍历取出每一条记录
//7) 输出的控制台上
System.out.println("删除记录条数:" +rows);
}catch(Exception e){
e.printStackTrace();
}
finally{
//8) 释放资源 一定要牢记
if(null!=ps){
ps.close();
}
if(null!=connection){
connection.close();
}

}

}


什么是数据库连接池?数据库连接池是存放数据库连接的容器,当容器被创建后,会向数据库申请数据库连接资源。我们从数据库连接池里获取数据库连接使用完毕后,会将连接还给容器。好处:

连接池复用了数据库连接,减少了访问数据库时,创建连接的开销,提高了程序的访问效率。

数据库连接池原理:

实现javax.sql包下的DataSource接口

获取数据库连接使用:

获取连接:getConnection()

归还连接:Connection.close()注意,使用数据库连接,不会关闭连接,而是归还。

一般来说,我们不用自己去实现数据库连接池,有很多很成熟的第三方产品可以使用。这里推荐阿里巴巴开源的Druid。

接下来我们看看怎么来使用它:

1.引入jar包

在pom.xml文件中增加

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>

2.定义配置文件

在src/main/resources下新建文件druid.properties

在文件中增加内容

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test01?characterEncoding=utf8
username=root
password=123456
initialSize=5
maxActive=10
maxWait=3000

说明:

driverClassName驱动名称 MYSQL 使用com.mysql.jdbc.Driver

url数据库访问url

username数据库用户名

password数据库密码

initialSize 初始化连接数

maxActive 最大活动连接数

maxWait 数据库访问超时时间 毫秒

我们再简单地封装数据库访问工具类:

package com.pz.web.demo.Util;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JDBCUtils {
// 1. 声明静态数据源成员变量
private static DataSource dataSource;

// 2. 创建连接池对象
static {
// 加载配置文件中的数据
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
Properties pp = new Properties();
try {
pp.load(is);
// 创建连接池,使用配置文件中的参数
dataSource = DruidDataSourceFactory.createDataSource(pp);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

// 3. 定义公有的得到数据源的方法
public static DataSource getDataSource() {
return dataSource;
}

// 4. 定义得到连接对象的方法
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}

// 5.定义关闭资源的方法
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {}
}

if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {}
}

if (conn != null) {
try {
conn.close();
} catch (SQLException e) {}
}
}

// 6.重载关闭方法
public static void close(Connection conn, Statement stmt) {
close(conn, stmt, null);
}
}


Spring JDBC 是Spring框架对jdbc的模板封装,能够简化数据访问的开发,支持使用对象访问数据库。

我们看看使用Spring JDBC 访问数据库的步骤:

1.引入依赖

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.1.2.RELEASE</version>
</dependency>

2.创建JdbcTemplate对象

3.使用JdbcTemplate提供的相关方法,访问数据库。JdbcTemplate支持以对象的方式访问数据库,数据库里的一条记录是唯一的,对象也是唯一的,我们可以简单的把数据库里的字段名,看作是domain类的属性名,数据库里的一行记录,看作是一个domain类的一个实例。

由于JdbcTemplate支持对象和查询结果集字段的小驼峰匹配或者是完全匹配,建议domain类命名遵循和数据库字段的小驼峰匹配规则(首字母小写如果遇到下划线,则每一个下划线后第一个字母大写)。

我们看到student表的字段如下:

所以我们定义的domian类如下:

package com.pz.web.demo.domain;

import java.util.Date;

/**
*
* @author pangzi
* 字段名和数据库字段命名规则
* 由于JdbcTemplate支持对象和查询结果集字段的小驼峰匹配或者是完全匹配,
* 建议domain类命名遵循和数据库字段的小驼峰匹配规则
*
*/
public class Student {
/**
* id编号
*/
private Long id;
/**
* 姓名
*/
private String name;
/**
* 性别
*/
private int sex ;

/**
* 年龄
*/
private int age ;

/**
* 入学日期
*/
private Date admissionDate;
/**
* 备注
*/
private String remark;

public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public Date getAdmissionDate() {
return admissionDate;
}
public void setAdmissionDate(Date admissionDate) {
this.admissionDate = admissionDate;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", sex=" + sex
+ ", age=" + age + ", admissionDate=" + admissionDate
+ ", remark=" + remark + "]";
}

}

JdbcTemplate提供的数据访问方法如下:

queryForObject:将查询结果封装为对象,注意查询结果只能有一条记录,如果无查询结果返回,程序可能异常

queryForList():将查询结果封装为List集合。

update();用于执行dml语句,增删改。

queryForMap():查询结果将结果集封装为map集合,将列名作为key,将值作为value,注意查询结果只能有一条记录

package com.pz.web.demo.jdbc;

import com.pz.web.demo.Util.JDBCUtils;
import com.pz.web.demo.domain.Student;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.sql.SQLException;
import java.util.Date;
import java.util.List;

/**
*
* @author pangzi
*
*/
public class SpringJDBCCRUDDemo {

private static JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());

/**
* 按id查询学生信息
* @param id
* @return
* @throws SQLException
*/
private static Student queryStudentById(Long id) throws SQLException{

Student student = null;
try {
String sql = " select * from student where ID = ?";
student = template.queryForObject(sql, new BeanPropertyRowMapper<Student>(Student.class), id);
} catch (DataAccessException e) {
e.printStackTrace();
}
return student;


}

/**
* 查询所有学生记录
* @return
* @throws SQLException
*/
private static List<Student> queryStudentList() throws SQLException{

String sql = " select * from student ";
return template.query(sql,new BeanPropertyRowMapper<Student>(Student.class));

}

/**
* 新增学生记录
* @param student
* @throws SQLException
*/
private static void addStudent(Student student ) throws SQLException{

String sql="insert into student(name,sex,age,admission_date,remark) values(?,?,?,?,?)";
template.update(sql,student.getName(),student.getSex(),student.getAge(),student.getAdmissionDate(),student.getRemark()
);

}

/**
* 修改学生记录
* @param student
* @throws SQLException
*/
private static void updateStudent(Student student) throws SQLException{

String sql="update student set name=?, sex=? ,admission_date=? ,remark =? where id=?";
template.update(sql,student.getName(),
student.getSex(),
student.getAdmissionDate()
,student.getRemark(),
student.getId()
);


}

/**
* 按id删除学生记录
* @param id
* @throws SQLException
*/
private static void deleteStudent(Long id ) throws SQLException{

String sql="delete from student where id=?";

template.update(sql,id
);
}

public static void main(String args[]){

try {
Student dbStundent=queryStudentById(2L);
System.out.println(dbStundent);
Student stundent = new Student();
stundent.setName("新来的");
stundent.setSex(1);
stundent.setAge(18);
stundent.setRemark("我用JDBCTemplate报名");
stundent.setAdmissionDate(new Date());
addStudent(stundent);
dbStundent.setName("我怎么变成新来的了");
updateStudent(dbStundent);
deleteStudent(8L);
List<Student> allStundents=queryStudentList();
System.out.println("==所有学生记录==");
for(Student s:allStundents){
System.out.println(s);
}

} catch (SQLException e) {
e.printStackTrace();
}

}

}

注意:JdbcTemplate所有方法支持的都是可变参数,参数个数是不固定的,每一个参数的传入顺序对应sql里的问号。