官方教程(包括 javase的基础部分):JDBC Basics

重新梳理、学习一下“Java连接数据库”相关的内容。

因为最开始没有认真学多线程和JDBC,一直在自己写的多线程程序中维持下面的错误写法:

  1. 多个线程共用一个connection
  2. connection只开不关

为什么上述做法是错误的呢? 可以参看这个帖子。- - “JDBC规范并未规定那三个对象必须是线程安全的,因此所有的JDBC厂商也不会去弄成线程安全的,正因为如此,所以就会有并发问题。” 、-- “ 并不是说不能把连接对象弄成成员变量,只是不能将其弄成成员变量后,在多线程环境下处于共享这些对象,如果同步处理得不好,那就会产生严重的连接泄漏。为了避免这种情况发生,仅在用时获取连接,用完后马上关掉。” -- “如果你对JDBC、多线程编程没有达到非常熟练的程度,还是老老实实地使用经典的JDBC代码结构。” -- 摘抄自csdn 火龙果被占用了

另外,connection只开不关很容易导致连接失效(mysql默认保持连接的时间是8小时,如果这个连接在8小时内无人访问的话,就会关闭这个连接。- -摘)

我把这些错误代码放在第一小节记录下来,作为java连接数据库的最原始版本,在这之后逐渐改良成可以适应各种场景的正确代码。

① DDL.sql

DROP TABLE IF EXISTS `profile`;
CREATE TABLE `profile` (
`profileId` BIGINT(20) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL,
`password` VARCHAR(50) NOT NULL,
`nickname` VARCHAR(50) NOT NULL,
`last_online` TIMESTAMP NULL DEFAULT NULL,
`gender` CHAR(1) NULL DEFAULT NULL,
`birthday` TIMESTAMP NULL DEFAULT NULL,
`location` VARCHAR(50) NULL DEFAULT NULL,
`joined` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`profileId`),
UNIQUE INDEX `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8;

profile表可以基本等价为user表来理解。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

② 对应的实体类Profile.java

package org.sample.entity;

import java.sql.Timestamp;

public class Profile {
private Long profileId;
private String username;
private String password;
private String nickname;
private Timestamp last_online;
private Character gender;
private Timestamp birthday;
private String location;
private Timestamp joined; public Profile() {
} public Long getProfileId() {
return profileId;
} public void setProfileId(Long profileId) {
this.profileId = profileId;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public String getNickname() {
return nickname;
} public void setNickname(String nickname) {
this.nickname = nickname;
} public Timestamp getLast_online() {
return last_online;
} public void setLast_online(Timestamp last_online) {
this.last_online = last_online;
} public Character getGender() {
return gender;
} public void setGender(Character gender) {
this.gender = gender;
} public Timestamp getBirthday() {
return birthday;
} public void setBirthday(Timestamp birthday) {
this.birthday = birthday;
} public String getLocation() {
return location;
} public void setLocation(String location) {
this.location = location;
} public Timestamp getJoined() {
return joined;
} public void setJoined(Timestamp joined) {
this.joined = joined;
} @Override
public String toString() {
return "Profile{" +
"profileId=" + profileId +
", username='" + username + '\'' +
", password='" + password + '\'' +
", nickname='" + nickname + '\'' +
", last_online=" + last_online +
", gender=" + gender +
", birthday=" + birthday +
", location='" + location + '\'' +
", joined=" + joined +
'}';
}
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

③ ConnectionFactory.java或者常说的Dbutil(错误代码 ↓

package org.sample.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ResourceBundle; /**
* 单线程适用,只开不关,反复用一个 Connection
*/
public class StaticConnectionFactory { private static ResourceBundle rb = ResourceBundle.getBundle("org.sample.db.db-config"); private static final String JDBC_URL = rb.getString("jdbc.url"); private static final String JDBC_USER = rb.getString("jdbc.username"); private static final String JDBC_PASSWORD = rb.getString("jdbc.password"); private static Connection conn = null; static {
try {
// Class.forName("org.gjt.mm.mysql.Driver");
// JDBC 4.0 之后(包括 JDBC 4.0)不再需要 class.forName ,详细查看 javaSE6 之后的 API
conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
} catch (SQLException e) {
throw new RuntimeException("Error connecting to the database", e);
}
} public static Connection getConnection() {
return conn;
} public static void setAutoCommit(boolean autoCommit) throws SQLException {
conn.setAutoCommit(autoCommit);
} public static void commit() throws SQLException {
conn.commit();
} public static void rollback() throws SQLException {
conn.rollback();
}
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

④ org/sample/db/db-config.properties

jdbc.url=jdbc:mysql://***.**.**.**:3306/profiles?characterEncoding=utf8
jdbc.username=root
jdbc.password=aaaaaaaaaaa

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

⑤ ProfileDAO.java

package org.sample.dao;

import org.sample.entity.Profile;

import java.util.List;

public interface ProfileDAO {

    int saveProfile(Profile profile);

    List<Profile> listProfileByNickname(String nickname);

    Profile getProfileByUsername(String username);

    int updateProfileById(Profile profile);

    int updatePassword(String username, String password);

    int updateLastOnline(String username);
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

⑥ ProfileDAOImpl.java(为了用“带资源的try”严重画蛇添足了。)

package org.sample.dao.impl;

import com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException;
import org.sample.dao.ProfileDAO;
import org.sample.db.StaticConnectionFactory;
import org.sample.entity.Profile; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List; // NotThreadSafe
public class ProfileDAOImpl implements ProfileDAO { private static final Connection conn = StaticConnectionFactory.getConnection(); @Override
public int saveProfile(Profile profile) {
int i = 0;
try (
PreparedStatement ps =
createPreparedStatementForSaveProfile(conn, profile);
) {
i = ps.executeUpdate();
} catch (SQLException e) {
if (!(e instanceof MySQLIntegrityConstraintViolationException)) {
e.printStackTrace();
}
}
return i;
} @Override
public List<Profile> listProfileByNickname(String nickname) {
List<Profile> profiles = new ArrayList<>();
try (
PreparedStatement ps =
createPreparedStatementForListProfileByNickname(conn, nickname);
ResultSet rs = ps.executeQuery();
) {
while (rs.next()) {
Profile profile = extractProfileFromResultSet(rs);
profiles.add(profile);
}
} catch (SQLException e) {
e.printStackTrace();
}
return profiles;
} @Override
public Profile getProfileByUsername(String username) {
Profile profile = null;
try (
PreparedStatement ps =
createPreparedStatementForGetProfileByUsername(conn, username);
ResultSet rs = ps.executeQuery();
) {
if (rs.next()) {
profile = extractProfileFromResultSet(rs);
}
} catch (SQLException e) {
e.printStackTrace();
}
return profile;
} @Override
public int updateProfileById(Profile profile) {
int i = 0;
try (
PreparedStatement ps =
createPreparedStatementForUpdateProfileById(conn, profile);
) {
i = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return i;
} @Override
public int updatePassword(String username, String password) {
int i = 0;
try (
PreparedStatement ps =
createPreparedStatementForUpdatePassword(username, password);
) {
i = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return i;
} @Override
public int updateLastOnline(String username) {
int i = 0;
try (
PreparedStatement ps =
createPreparedStatementForUpdateLastOnline(username);
) {
i = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return i;
} private Profile extractProfileFromResultSet(ResultSet rs) throws SQLException {
Profile profile = new Profile();
profile.setBirthday(rs.getTimestamp("birthday"));
profile.setJoined(rs.getTimestamp("joined"));
profile.setLast_online(rs.getTimestamp("last_online"));
profile.setLocation(rs.getString("location"));
profile.setNickname(rs.getString("nickname"));
profile.setPassword(rs.getString("password"));
profile.setProfileId(rs.getLong("profile_id"));
profile.setUsername(rs.getString("username"));
if (rs.getString("gender") != null) {
profile.setGender(rs.getString("gender").charAt(0));
}
return profile;
} private PreparedStatement createPreparedStatementForSaveProfile(Connection conn, Profile profile) throws SQLException {
String sql = "INSERT INTO `profiles`.`profile` (`username`, `password`, `nickname`) " +
"VALUES (?, ?, ?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, profile.getUsername());
ps.setString(2, profile.getPassword());
ps.setString(3, profile.getNickname());
return ps;
} private PreparedStatement createPreparedStatementForListProfileByNickname(Connection conn, String nickname) throws SQLException {
String sql = "SELECT `profile_id`, `username`, `password`, `nickname`, `last_online`, `gender`, `birthday`, `location`, `joined`" +
"FROM `profiles`.`profile`" +
"WHERE `nickname`=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, nickname);
return ps;
} private PreparedStatement createPreparedStatementForGetProfileByUsername(Connection conn, String username) throws SQLException {
String sql = "SELECT `profile_id`, `username`, `password`, `nickname`, `last_online`, `gender`, `birthday`, `location`, `joined`" +
"FROM `profiles`.`profile`" +
"WHERE `username`=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, username);
return ps;
} private PreparedStatement createPreparedStatementForUpdateProfileById(Connection conn, Profile profile) throws SQLException {
String sql = "UPDATE `profiles`.`profile`" +
"SET `nickname`=?, `gender`=?, `birthday`=?, `location`=? " +
"WHERE `profile_id`=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, profile.getNickname());
ps.setString(2, profile.getGender() != null ? String.valueOf(profile.getGender()) : null);
ps.setTimestamp(3, profile.getBirthday());
ps.setString(4, profile.getLocation());
ps.setLong(5, profile.getProfileId());
return ps;
} private PreparedStatement createPreparedStatementForUpdatePassword(String username, String password) throws SQLException {
String sql = "UPDATE `profiles`.`profile`" +
"SET `password`=? " +
"WHERE `username`=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, password);
ps.setString(2, username);
return ps;
} private PreparedStatement createPreparedStatementForUpdateLastOnline(String username) throws SQLException {
String sql = "UPDATE `profiles`.`profile`" +
"SET `last_online`=CURRENT_TIMESTAMP " +
"WHERE `username`=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, username);
return ps;
}
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

⑦ ProfileDAOTest.java

package org.sample.dao;

import org.junit.Test;
import org.sample.dao.impl.ProfileDAOImpl;
import org.sample.db.StaticConnectionFactory;
import org.sample.entity.Profile; import java.sql.SQLException;
import java.util.List; import static org.junit.Assert.*; public class ProfileDAOTest { private ProfileDAO profileDAO = new ProfileDAOImpl(); private static final String USER_NAME = "hello123"; @Test
public void saveProfile() {
Profile profile = new Profile();
profile.setUsername(USER_NAME);
profile.setPassword("231231232");
profile.setNickname("jack");
int i = profileDAO.saveProfile(profile);
System.out.println(i);
} @Test
public void listProfileByNickname() {
List<Profile> profiles = profileDAO.listProfileByNickname("123");
} @Test
public void getProfileByUsername() {
Profile existProfile = profileDAO.getProfileByUsername(USER_NAME);
Profile notExistProfile = profileDAO.getProfileByUsername(USER_NAME + "321");
assertNotNull(existProfile);
assertNull(notExistProfile);
} @Test
public void updateProfileById() {
Profile profile = profileDAO.getProfileByUsername(USER_NAME);
int i = profileDAO.updateProfileById(profile);
assertEquals(1, i); // 即便没改变值,但是还是会重新set一遍,因此影响行数还是一行 profile.setGender('f');
profile.setNickname("www" + Math.random());
int j = profileDAO.updateProfileById(profile);
assertEquals(1, j);
} @Test
public void updatePassword() {
profileDAO.updatePassword(USER_NAME, "www" + Math.random());
} @Test
public void updateLastOnline() throws SQLException {
try {
StaticConnectionFactory.setAutoCommit(false);
profileDAO.getProfileByUsername(USER_NAME);
profileDAO.updateLastOnline(USER_NAME);
StaticConnectionFactory.commit();
} catch (SQLException e) {
e.printStackTrace();
try {
StaticConnectionFactory.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
StaticConnectionFactory.setAutoCommit(true);
}
}
}

Java连接数据库 #01# JDBC单线程适用的更多相关文章

  1. Java连接数据库 #02# JDBC经典套路

    内容索引 LocalConnectionFactory.java LocalConnectionProxy.java ProfileDAO.java-2.0 ProfileDAOImpl.java-2 ...

  2. java连接数据库(jdbc)的标准规范

    java连接数据库的标准规范 JDBC全称:java database connectivity ,是sun公司提供的Java连接数据库的标准规范. localhost和127.0.0.1 都是表示当 ...

  3. 学数据库你竟然不用用JAVA写代码,可惜你遇到了我! JAVA连接数据库(JDBC)的安装使用教程

    Step 1 你得有Eclipse 没有出门右拐,我教不了你. Step 2 你得有Mysql MySQL的详细安装过程,我在另一篇博客中给出.戳我 Step 3 安装JDBC 可以去官网下,如果用的 ...

  4. 从零开始学JAVA(04)-连接数据库MSSQL(JDBC准备篇)

    在JAVA中可以使用JDBC连接数据库,不管是哪种数据库,首先必须下载驱动,包括Windows的MSSQL. 1.下载MSSQL的JDBC驱动,可以通过百度“Microsoft JDBC Driver ...

  5. 完整java开发中JDBC连接数据库代码和步骤[申明:来源于网络]

    完整java开发中JDBC连接数据库代码和步骤[申明:来源于网络] 地址:http://blog.csdn.net/qq_35101189/article/details/53729720?ref=m ...

  6. java连接数据库(jdbc)调用配置文件

    各种语言都有自己所支持的配置文件,后缀名“.properties”结尾的就是其中之一. 在java连接数据库时,采取读取配置文件的方式,来获取数据库连接. 新建jdbc.properties文件,内容 ...

  7. Java数据库连接技术——JDBC

    大家好,今天我们学习了Java如何连接数据库.之前学过.net语言的数据库操作,感觉就是一通百通,大同小异. JDBC是Java数据库连接技术的简称,提供连接各种常用数据库的能力. JDBC API ...

  8. Java连接数据库的辣几句话

    Java连接数据库的辣几句话 1.java连接Oracle数据库 使用以下代码三个步骤: 1.下载ojdbc.jar包并导入项目中.附下载地址:http://download.csdn.net/det ...

  9. servlet中Java连接数据库后的基本操作

    servlet中Java连接数据库后的基本操作 在eclipse中新建一个工程:login 在Server中新建一个服务器,基本的操作不用说了,在前两天的笔记中可以找到; 需要知道数据库的用户名和密码 ...

随机推荐

  1. golang 中处理大规模tcp socket网络连接的方法,相当于c语言的 poll 或 epoll

    https://groups.google.com/forum/#!topic/golang-nuts/I7a_3B8_9Gw https://groups.google.com/forum/#!ms ...

  2. PHP策略模式1

    [IUser.php] <?php /** * 策略模式 * 将一组特定的行为和算法封装成类,用来适应某些特定的上下文环境,实现从硬编码到解耦 * 应用举例:电商系统针对不同性别跳转到不同的商品 ...

  3. DL中train\dev\test集

    转自:https://blog.csdn.net/l8947943/article/details/80328721 training set:训练集是用来训练模型的.遵循训练集大,开发,测试集小的特 ...

  4. 【转】JsonPath教程

    https://blog.csdn.net/koflance/article/details/63262484 1. 介绍 类似于XPath在xml文档中的定位,JsonPath表达式通常是用来路径检 ...

  5. Tensorflow实现手写体分类(含dropout)

    一.手写体分类 1. 数据集 import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data im ...

  6. JAVA编程思想学习笔记5-chap13-15-斗之气5段

    1.String对象不可变,一旦发生字符变换或者变长度,一定是新建了一个String private final char value[]; 2.字符串+与+=:唯二操作符重载 "aaa&q ...

  7. JAVA编程思想学习笔记4-chap10-12-斗之气4段

    1.内部类:Iterator 2..this生成对外部类的引用 3..new:通过外部类对象创建内部类对象 package com.chengjie; public class TestInnerCl ...

  8. 当我的url请求会变成jsp页面路径时的解决办法

    @RequestMapping(value="shippingOrder") $.post("/ezsh/orderAd/shippingOrder",para ...

  9. node.js初识02

    node.js相较于那些老的服务器语言,他的优势在于,节省了I/O的时间,主要的特点是单线程,非阻塞和事件驱动,其实三个说的是同一个事情,相较于多线程而言,单线程的特点是,使用的那一条线程的cpu的利 ...

  10. Linux(Centos)服务器配置node项目

    以阿里云服务器,CentOS系统为例 上一节已经提到怎么安装nodejs,以下是以vue项目为例 步骤: (1)首先安装vue脚手架@vue/cli, 官网参考 vue-cli3.x [root@lu ...