官方教程(包括 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. Request实例

    Request常用方法        getRequestURL方法返回客户端发出请求时的完整URL. getRequestURI方法返回请求行中的资源名部分. getQueryString 方法返回 ...

  2. Python复习基础篇

    4.4 使用列表的一部分   Python切片(切片就是取值的呗) print(players[0:3])  中括号,冒号隔开,顾头不顾尾. print([:4])    从开始取还是会顾尾的 pri ...

  3. node微信公众号开发--设置自定义菜单

    var request = require("request"); const querystring = require("querystring"); re ...

  4. R安装package报ERROR: a 'NAMESPACE' file is required

    R安装package报错: [root@Hadoop-NN-01 mysofts]# R CMD INSTALL trimcluster_0.1-1.tar.gz * installing to li ...

  5. iOS UI进阶-6.0 手势

    给每个页面添加手势,只需要统一设置不是根控制器的页面,都增加手势.需要自定义导航控制器 1.继承代理 @interface BSNavigationController ()<UIGesture ...

  6. cocos2d-x JS 加载播放Studio帧动画的两种方法

    昨天懵逼的搞了两个多小时(百度无果/没看出什么矛头),自己琢磨总算搞出来了   1.  var levelUpJson = ccs.load("res/LevelUp.json") ...

  7. response.sendRedirect(url)与request.getRequestDispatcher(url).forward(request,response)的区别

    response.sendRedirect(url)跳转到指定的URL地址,产生一个新的request,所以要传递参数只有在url后加参数,如: url?id=1.request.getRequest ...

  8. Git常用的命令

    常用 Git 命令清单   作者: 阮一峰 日期: 2015年12月 9日 原文地址:http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.ht ...

  9. Unity shader学习之Alpha Test的阴影

    Alpha Test的阴影, shader如下: // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClip ...

  10. leetCodelinked-list-cycle-ii找到链表的环

    题目 Given a linked list, return the node where the cycle begins. If there is no cycle, return null. N ...