java jdbc 保存_Java JDBC

一、什么是JDBC?

JDBC就是Java链接数据库的一种方式,一种规则。

二、为什么要学JDBC?

因为你的Java项目需要链接数据库保存数据。目前来说,JDBC是最底层的东西,当前市面上流行的最火的JDBC封装有hibernate和mybatis,这俩都可以简化一些操作。其实他俩底层还是JDBC,就是做了个封装,让人使用更简单而已。为了深入了解hibernate和mybatis,JDBC还是要学的。

三、执行增删改语句

先来尝试一条语句:

packagecom.StadyJava.day2;import org.junit.*;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.Statement;public classJDBCDemo {

@Testpublic void Con() throwsException {

String sql="insert SysUser values('201408090009',123,'李信','男','王者荣耀','shuyunquan@qq.com','老师')";//1.加载注册Mysql驱动

Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");//2.链接数据库,获取链接对象

Connection con = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;databaseName=Design;user=sa;password=123");//3.创建语句对象

Statement st=con.createStatement();//4.执行SQL语句

int row=st.executeUpdate(sql);//5.释放资源

st.close();

con.close();

System.out.println(row);

}

}

是可以成功的,这里我使用的Jnuit测试单元来做的,不是Main方法,这个以前的博客介绍过。还有JDBC的SQL Server链接包,在Maven里面下载就好了。Maven不会的自己学。

executeUpdate方法可以执行增删改和创建表的语句。

四、执行查询语句

增删改完成了,现在来看看查询语句是怎么写的,首先要知道,JDBC查询会返回一个结果集 ResultSet 这个结果集就像一个游标一样,我们可以逐层访问他里面的内容。

首先我写一个SQL语句

select Name,Sex from SysUser

我查询两个字段,内容是这样的

Name Sex

许嵩 男

林俊杰  男

陈亮 男

缪斯 女

魁拔 女

范锁 男

李信 男

看看代码,换成 executeQuery 了

packagecom.StadyJava.day2;import org.junit.*;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.ResultSet;importjava.sql.Statement;public classJDBCDemo {

@Testpublic void Con() throwsException {

String sql="select Name,Sex from SysUser";//1.加载注册Mysql驱动

Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");//2.链接数据库,获取链接对象

Connection con = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;databaseName=Design;user=sa;password=123");//3.创建语句对象

Statement st=con.createStatement();//4.执行SQL语句

ResultSet rs=st.executeQuery(sql);//处理结果集

while(rs.next()){

String name=rs.getString("Name");

String sex=rs.getString("Sex");

System.out.println(name+','+sex);

}//5.释放资源

rs.close();

st.close();

con.close();

}

}

上面的都是最简单最简单的JDBC操作,工作中我们完全不会这样去操作,去写。就比如这个增删改,写在一个方法里面,那我们多处使用这个,岂不是每个方法都要写?

这样代码就重复了,违背了ORP单一原则。.Net里面是有类库这一概念的,就是封装成一个类库,使用的时候调用就可以了。

Java这里我们也来对JDBC进行一个封装。

一、创建操作接口

写接口比较规范,我规定你对我的这个类进行的操作就是增删改查,下面写实现类

packagecom.StadyJava.DAODemo.dao;importcom.StadyJava.DAODemo.domain.User;importjava.util.List;public interfaceIUserDAO {/*** 保存操作

*@paramuser*/

voidsave(User user);/*** 删除操作

*@paramid*/

voiddelete(Long id);voidupdate(Long id,User newuser);

User get(Long id);

ListlistAll();

}

可以看到,我的接口写了注释,/** 然后敲回车就是对方法的注释,这样有一个好处,就是我的实现类,你在写的时候,鼠标放上去,会有提示。

二、创建Model类

我创建了一个User的Model类:

packagecom.StadyJava.DAODemo.domain;importlombok.Getter;importlombok.Setter;

@Setter@Getterpublic classUser {private longid;privateString name;privateString sex;

@OverridepublicString toString() {return "User{" +

"id=" + id +

", name='" + name + '\'' +

", sex=" + sex +

'}';

}

}

我使用的Lombok自动创建的属性构造器

三、连接池

什么是连接池?可以看到,我们上面是直接写了一个Connection,使用完毕之后直接释放的。这样不好,因为Connection连接创建非常耗费资源,你用一下就释放了,再用再连接,这样不行的。所以连接池的概念就出来了,连接池里有默认的几个Connection,谁用谁来获取,使用完成之后释放,这个释放的意思是把Connection资源归还给连接池,并没有真正的释放。这样保证了效率。

50d3b385ce1c32c2b7874ea40eea140b.png

6f9d5bd2ba79c388f4d8fccc3fd5446a.png

连接池有两种是需要介绍一下的

1.Apache做的DBCP

2.阿里做的号称世界上最快的druid

先来介绍DBCP连接池

packagecom.StadyJava.DAODemo.util;

importcom.StadyJava.DAODemo.JunitDAO;

importorg.apache.commons.dbcp2.BasicDataSourceFactory;

import javax.sql.*;

import java.sql.*;

importjava.util.Properties;

public classDBCPUtil {

//创建一个连接池对象,因为我的连接池对象只需要创建一次即可,所以我写在静态代码块里

private static DataSource ds=null;

static{

Properties properties=newProperties();

try{

properties.load(DBCPUtil.class.getClass().getResourceAsStream("/test"));

ds=BasicDataSourceFactory.createDataSource(properties);

} catch(Exception e) {

e.printStackTrace();

}

}

public staticConnection getConn(){

try{

returnds.getConnection();

} catch(Exception e) {

e.printStackTrace();

}

return null;

}

//释放资源

public static voidclose(Connection conn, Statement st, ResultSet rs) {

try{

if (rs != null) {

rs.close();

}

}catch(Exception e){

e.printStackTrace();

}finally{

try{

if (st != null) {

st.close();

}

}catch(Exception e){

e.printStackTrace();

}finally{

try{

if (conn != null) {

conn.close();

}

}catch(Exception e){

e.printStackTrace();

}

}

}

}

}

然后在Junit测试写了一个调用的方法:

@Test

public void DBCPTest() throwsException {

//测试链接池

Connection conn=DBCPUtil.getConn();

//这里的SQL暂时先写死

PreparedStatement ps=conn.prepareStatement("select AccountNumber from SysUser ");

ResultSet rs=ps.executeQuery();

while(rs.next()) {

System.out.println(rs.getLong("AccountNumber"));

}

DBCPUtil.close(conn,ps,rs);

}

运行结果:

69682265f1958ed2786686fae2b2623b.png

下面是DruId连接池,其实都差不多

packagecom.StadyJava.DAODemo.util;

importcom.alibaba.druid.pool.DruidDataSource;

importcom.alibaba.druid.pool.DruidDataSourceFactory;

importorg.apache.commons.dbcp2.BasicDataSourceFactory;

importjavax.sql.DataSource;

importjava.sql.Connection;

importjava.sql.ResultSet;

importjava.sql.Statement;

importjava.util.Properties;

public classDruidUtil {

//创建一个连接池对象,因为我的连接池对象只需要创建一次即可,所以我写在静态代码块里

private static DataSource ds=null;

static{

Properties properties=newProperties();

try{

properties.load(DBCPUtil.class.getClass().getResourceAsStream("/test"));

ds=DruidDataSourceFactory.createDataSource(properties);

} catch(Exception e) {

e.printStackTrace();

}

}

public staticConnection getConn(){

try{

returnds.getConnection();

} catch(Exception e) {

e.printStackTrace();

}

return null;

}

//释放资源

public static voidclose(Connection conn, Statement st, ResultSet rs) {

try{

if (rs != null) {

rs.close();

}

}catch(Exception e){

e.printStackTrace();

}finally{

try{

if (st != null) {

st.close();

}

}catch(Exception e){

e.printStackTrace();

}finally{

try{

if (conn != null) {

conn.close();

}

}catch(Exception e){

e.printStackTrace();

}

}

}

}

}

这里面我们的Statement换成了PreparedStatement,因为PreparedStatement可以去拼接SQL,是动态的SQL,statement仅仅是静态的。

PreparedStatement和statement都可以表示语句对象:

Preparedstatement相对于statement的优势:

1):拼接SQL上,操作更简单.

2):性能会更加高效,但是需要取决于数据库服务器是否支持.

MySQL:不支持 Oracle:支持

四、创建JDBC的模板

因为增删改的操作大部分都是一样的,所以建立一个模板比较好,这个模板里面就两个方法,一个是update,主要是增删改。一个是queue,主要是查询。

这里我写了T ,自定义泛型,主要是传入的类型是什么,我获取的返回类型就是什么。

packagecom.StadyJava.DAODemo.util;importcom.StadyJava.DAODemo.dao.IResultSetHandler;importjava.sql.Connection;importjava.sql.PreparedStatement;importjava.sql.ResultSet;public classJDBCTemplate {/*** 操作增删改的模板

*@paramsql

*@paramparams

*@return

*/

public static intupdate(String sql,Object... params){

Connection conn=null;

PreparedStatement ps=null;try{

conn=DruidUtil.getConn();

ps=conn.prepareStatement(sql);for (int i = 0; i < params.length; i++) {

ps.setObject(i+1,params[i]);

}returnps.executeUpdate();

}catch(Exception e) {

e.printStackTrace();

}finally{

DruidUtil.close(conn,ps,null);

}return 0;

}/*** 操作查询的模板

*@paramsql

*@paramparams

*@return

*/

public static T queue(String sql, IResultSetHandlerrsh, Object... params) {

Connection conn=null;

PreparedStatement ps=null;

ResultSet rs=null;try{

conn=DruidUtil.getConn();

ps=conn.prepareStatement(sql);for (int i = 0; i < params.length; i++) {

ps.setObject(i+1,params[i]);

}

rs=ps.executeQuery();returnrsh.handle(rs);

}catch(Exception e) {

e.printStackTrace();

}finally{

DruidUtil.close(conn,ps,rs);

}throw new RuntimeException("查询结果有错误");

}

}

五、创建接口的实现类

我们第一步写了一个操作的规范接口,现在来实现一下

packagecom.StadyJava.DAODemo.dao.impl;importcom.StadyJava.DAODemo.dao.IResultSetHandler;importcom.StadyJava.DAODemo.dao.IUserDAO;importcom.StadyJava.DAODemo.domain.User;importcom.StadyJava.DAODemo.util.JDBCTemplate;importcom.StadyJava.DAODemo.util.JDBCUtil;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.ResultSet;importjava.sql.Statement;importjava.util.ArrayList;importjava.util.List;public class UserDAOImpl implementsIUserDAO {

@Overridepublic voidsave(User user) {

JDBCTemplate.update("insert SysUser (AccountNumber,Name,Sex) values(?,?,?)",user.getId(),user.getName(),user.getSex());

}

@Overridepublic voiddelete(Long id) {

JDBCTemplate.update("delete SysUser where AccountNumber=?,Name=?,Sex=?",id);

}

@Overridepublic voidupdate(Long id, User newuser) {

JDBCTemplate.update("update SysUser set AccountNumber=?, Name=? ,Sex=? where AccountNumber=?",id,newuser.getId(),newuser.getName(),newuser.getSex());

}

@OverridepublicUser get(Long id) {return JDBCTemplate.queue("select * from SysUser where AccountNumber=?",newUserResultSetHandler(),id);

}

@Overridepublic ListlistAll() {return JDBCTemplate.queue("select AccountNumber,Name,Sex from SysUser",newUserResultSetHandler());

}

}//结果集接口的实现类

class UserResultSetHandler implements IResultSetHandler>{

@Overridepublic List handle(ResultSet rs) throwsException {

List list=newArrayList();while(rs.next()) {

User user=newUser();

user.setId(rs.getLong("AccountNumber"));

user.setName(rs.getString("Name"));

user.setSex(rs.getString("Sex"));

list.add(user);

}returnlist;

}

}

下面写了一个查询的结果集的实现类,可以发现,我的 get()  方法其实是报错的,因为我的返回类型不一致了,这里就牵涉出了另一个问题,还要对查询的结果集的实现类再次进行重构,但是这个重构使用了Java的内省机制,还要求你的User Model类的字段必须和数据库里面的字段一致,这里我懒得改了,直接贴出代码

packagecom.StadyJava.DAODemo.util;importcom.StadyJava.DAODemo.dao.IResultSetHandler;importjava.beans.BeanInfo;importjava.beans.Introspector;importjava.beans.PropertyDescriptor;importjava.sql.ResultSet;public class BeanHandler implements IResultSetHandler{private ClassclassType;public BeanHandler(ClassclassType){this.classType=classType;

}

@Overridepublic T handle(ResultSet rs) throwsException {//1.创建对应类的一个对象

T obj=classType.newInstance();//2.使用内省机制取出数据

BeanInfo beanInfo=Introspector.getBeanInfo(classType,Object.class);

PropertyDescriptor [] pds=beanInfo.getPropertyDescriptors();if(rs.next()) {for(PropertyDescriptor pd : pds) {//获取对象的属性名

String columnName=pd.getName();

Object val=rs.getObject(columnName);//3.调用对象的setter方法

pd.getWriteMethod().invoke(obj,val);

}

}returnobj;

}

}

packagecom.StadyJava.DAODemo.util;importcom.StadyJava.DAODemo.dao.IResultSetHandler;importjava.beans.BeanInfo;importjava.beans.Introspector;importjava.beans.PropertyDescriptor;importjava.sql.ResultSet;importjava.util.ArrayList;importjava.util.List;public class BeanListHandler implements IResultSetHandler>{private ClassclassType;public BeanListHandler(ClassclassType){this.classType=classType;

}

@Overridepublic List handle(ResultSet rs) throwsException {

List list = new ArrayList<>();while(rs.next()) {//1.创建对应类的一个对象

T obj =classType.newInstance();

list.add(obj);//2.使用内省机制取出数据

BeanInfo beanInfo = Introspector.getBeanInfo(classType, Object.class);

PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();for(PropertyDescriptor pd : pds) {//获取对象的属性名

String columnName =pd.getName();

Object val=rs.getObject(columnName);//3.调用对象的setter方法

pd.getWriteMethod().invoke(obj, val);

}

}returnlist;

}

}

然后调用的时候,这样写就好了,这是终极的写法

@OverridepublicUser get(Long id) {return JDBCTemplate.queue("select * from SysUser where AccountNumber=?",new BeanHandler<>(User.class),id);

}

传一个你的类进去,就可以了,那个查询结果集的实现类就可以删了

我的实现很简单,就是使用Junit写的实现类

@Testpublic voidtestGetList() {

System.out.println(userDAO.listAll());

}

@Testpublic voidtestGet() {

System.out.println(userDAO.get(201408090001L));

}

Junit很好用,大家一定要用起来。

还有一些其他的知识需要了解一下:

事务

所谓的事务,理解之前先讲一个例子。银行转账的问题,比如我买许嵩的专辑,给许嵩转账。那么我转账分为几个步骤:

1.我的账户钱减少

2.许嵩账户钱增加

3.完成

这三步必须是顺序进行的,假如现在在第二步的时候断电了,我的钱没了,许嵩的钱却没有增加。这种情况显然是不合理的。所以事务的概念就出来了。所谓的事务,就是把几个步骤当做一个。

只要有一个失败,那么全部失败。必须全部成功,那才算成功。

事务这个研究之后再另外写一篇文章。现在这里就简单的介绍一下。

下面是一些简单的java代码:

//事务,手动写一个

Connection conn=null;//关闭事务的自动提交

conn.setAutoCommit(false);//提交事务

conn.commit();//回滚事务

conn.rollback();

批处理

讲一下什么是批处理,在执行SQL语句的时候,目前都是一条一条的执行的,这样是很麻烦的,效率也很低。可以举个例子了解一下。例如公交车,明明有200个座位,但是一次却只拉一个人到目的地。

这样假如我有3000人,那就要3000次。效率可谓是低下了。所以批处理就是一次执行多条语句。

公交车每次拉满200人,这样3000人只需要15次就完事了。这就是批处理的意义所在了。可以看一下代码是怎么写的,这里只简单的介绍一下语句。

//批处理

PreparedStatement ps=null;for (int i = 0; i < 1000; i++) {

String sql="insert 表 values(?,?)";

ps.addBatch();if (i%200 == 0) {

ps.executeBatch();//执行批量操作

ps.clearBatch(); //清空缓存

ps.clearParameters();//清除参数

}

}

效率方面

d44b5fe63aa66656e28327e3c9f12a7e.png

Mysql JDBC 版本 5.1.13开始,效率提高了很多。版本旧的效率不咋滴。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值