官方教程(包括 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 中的 sizeof 以及 golang中的 union

    golang 中的 sizeof: 1: int(unsafe.Sizeof(uint32(0))) 2: int(reflect.TypeOf(uint32(0)).Size()) golang中的 ...

  2. Visual Studio Code的常用快捷键

    一.Visual Studio Code简介 Visual Studio Code是个牛逼的编辑器,启动非常快,完全可以用来代替其他文本文件编辑工具.又可以用来做开发,支持各种语言,相比其他IDE,轻 ...

  3. SQL中查询前几条或者中间某几行数据limit

    SELECT * FROM table  LIMIT [offset,] rows | rows OFFSET offset 使用查询语句的时候,要返回前几条或者中间某几行数据,用Llimit   可 ...

  4. jenkins 的一个BUG

    最近更新了一批jenkin插件,更新完问题来了全局设置无法保存了... 报错如下 Stack trace net.sf.json.JSONException: null object at net.s ...

  5. 【LeetCode每天一题】Find First and Last Position of Element in Sorted Array(找到排序数组中指定元素的开始和结束下标)

    Given an array of integers nums sorted in ascending order, find the starting and ending position of ...

  6. Java Selenium - 元素定位(一)

    一,单个元素对象定位 Webdriver通过findElement() , findElements()等方法调用By对象来定位和查询元素 , findElement()返回一个元素对象,否则抛出异常 ...

  7. \Temporary ASP.NET Files\root\文件不断增长,如何处理?

    很久没有写博了.最近半年除了忙活布置新家和过年期间走亲访友之外,都是在公司处理一些项目中的杂事:连家里买的很多书都停下来没看了,感觉这段时间在事业和学习上一直都是忙忙碌碌,却又碌碌无为. 吐槽完,说正 ...

  8. 解决mysql的内存表“table is full”错误

    最后参考http://blog.sina.com.cn/s/blog_6942a1590101429h.html 来解决,摘录下核心 后来GOOGLE得知,需要重建该表才可以. 1. 设置新的参数 m ...

  9. 安装python sklearn经验总结

    1. 需要安装 numpy, scipy, 和sklearn和ipython,Cython sklearn,ipython, Cython都可以通过pip来安装.但scipy,可能还有numpy必须通 ...

  10. Vue开始

    Vue搭建项目 搭建VUe项目之前需要先安装脚手架,不然项目搭建完会有警告. 最后稍等一定的时间,运行结果如下: 出现上述提示,是因为我们没有先安装vue-cli,接下来,我们安装vue-cli 安装 ...