猿进化系列12——一文快速学会数据库访问
看完上一个章节,相信你已经掌握了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里的问号。