单例模式意图是
一个类只含有一个实例,并且只提供一个访问它的全局访问点。
我们在连接数据库时,需要创建数据库连接,而数据库的Connection是非常珍贵的资源,必须要重用,这时,我们可以用单例模式来创建Connection。
以下例子以mysql数据库连接举例。
首先,为了更方便的修改读取数据库的配置信息,我们将连接数据库时使用的驱动,数据库的用户名及密码、url地址存放于一个properties文件中(名称为db.properties)。
db.url=jdbc:mysql://localhost:3306/aa?useSSL=false&serverTimezone=UTC
db.user=root
db.password=123456
db.driver=com.mysql.cj.jdbc.Driver
建一个单例类Dbinfo 用于读取db.properties配置文件的信息,内容如下:
package com.icss.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;
public class Dbinfo {
private static Dbinfo dbinfo;//唯一的对象
private String url;
private String username;
private String password;
private String driver;
public String getUrl() {
return url;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getDriver() {
return driver;
}
//构造函数私有化
private Dbinfo() {
String fname = Dbinfo.class.getResource("/").getPath() + "/db.properties";
InputStream in = null;
try {
in = new FileInputStream(new File(fname));
Properties pro = new Properties();//Properties类表示一组持久的属性。 Properties可以保存到流中或从流中加载。 属性列表中的每个键及其对应的值都是一个字符串。
pro.load(in);//从输入字节流读取属性列表(键和元素对)。
//读取db.properties文件中的内容复制给当前唯一对象的属性
this.url = pro.getProperty("db.url");
this.password = pro.getProperty("db.password");
this.username = pro.getProperty("db.user");
this.driver = pro.getProperty("db.driver");
} catch (Exception e) {
e.printStackTrace();
}finally {
if(in != null) {
try {
in.close(); //关闭
} catch (Exception e2) {
}
}
}
}
//唯一的一个对象
static {
dbinfo = new Dbinfo();
}
public static Dbinfo instance() {
return dbinfo;
}
}
在dao层连接数据库时直接调用Dbinfo 类的instance()函数,并获取连接
package com.icss.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import com.icss.util.Dbinfo2;
public class BaseDao {
protected Connection conn;
public Connection getConn() {
return conn;
}
public void setConn(Connection conn) {
this.conn = conn;
}
public void openConnection() throws ClassNotFoundException,SQLException {
//conn必须可以多次重用
if(this.conn == null || this.conn.isClosed() ) {
//加载mysql的数据库驱动 包名+类名
Class.forName(Dbinfo.getDriver());
conn = DriverManager.getConnection(Dbinfo.getUrl(), Dbinfo.getUsername(), Dbinfo.getPassword());
}
}
public void closeConnection() {
if(conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.icss.dao;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import com.icss.dto.UserStu;
import com.icss.entity.User;
import com.icss.util.Log;
public class UserDao extends BaseDao{
public User login3(String uname,String pwd) throws Exception {
Log.logger.info("userdao:" + uname + "," + pwd);
User user = null;
this.openConnection();
String sql = "select * from tuser where uname=? and pwd=?";
PreparedStatement ps = this.conn.prepareStatement(sql);
ps.setString(1, uname);
ps.setString(2, pwd);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
int role = rs.getInt("role");
user = new User();
user.setUname(uname);
user.setPwd(pwd);
user.setRole(role);
break;
}
rs.close();
ps.close();
return user;
}
}
这样就实现了数据库连接的单例模式,以下为余下代码示例:
逻辑层(biz)
package com.icss.biz;
import com.icss.dao.UserDao;
import com.icss.dto.UserStu;
import com.icss.entity.User;
public class UserBiz {
public User login3(String uname,String pwd) throws Exception{
User user = null;
UserDao dao = new UserDao();
try {
user = dao.login3(uname, pwd);
} catch (Exception e) {
//异常处理后,二次抛出异常
throw e;
}finally {
//关闭数据库链接
dao.closeConnection();
}
return user;
}
实体类(entity):
package com.icss.entity;
public class User {
private String uname;
private String pwd;
private int role;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public int getRole() {
return role;
}
public void setRole(int role) {
this.role = role;
}
}
测试(ui):
package com.icss.ui;
import com.icss.biz.UserBiz;
import com.icss.dto.UserStu;
import com.icss.entity.IRole;
import com.icss.util.Log;
public class TestLogin {
/**
* UI层必须捕获并处理异常
* @param uname
* @param pwd
*/
public static void login(String uname,String pwd) {
UserBiz biz = new UserBiz();
try {
UserStu user = biz.login3(uname, pwd);
if(user == null) {
System.out.println(uname + ",登陆失败,用户名或密码错误");
}else {
System.out.println(uname + ",登陆成功,他是" + user.getName() );
if(user.getRole() == IRole.ADMIN) {
System.out.println(uname + "是管理员");
}else if(user.getRole() == IRole.COMMON_USER) {
System.out.println(uname + "是普通用户");
}else if(user.getRole() == IRole.VIP_USER) {
System.out.println(uname + "是VIP");
}else {
System.out.println(uname + " 身份信息不明");
}
}
} catch (Exception e) {
//异常记录日志,提示用户友好信息
Log.logger.error(e.getMessage(),e);
System.out.println("网络链接繁忙,请稍后再试");
}
}
public static void main(String[] args) {
TestLogin.login("16970744526", "123456");
}
}
这样,单例模式,就成功的应用于数据库连接了。