一、JNDI数据库连接通用数据访问层(JNDIDBManager)
代码下:
package com.myphome.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
/**
* JNDI数据库连接通用数据访问层
* */
public class JNDIDBManager {
//mySql连接固定参数和数据源名称字符串(分为两部分"java:comp/env"为JavaEE默认路径,"jdbc/blog"为DataSource名)
private static final String MYSQL_DATASOURCE_STRING="java:comp/env/jdbc/blog";
//sqlserver连接固定参数和数据源名称字符串
private static final String SQLSERVER_DATASOURCE_STRING="java:comp/env/googlesnack";
//oracle连接固定参数和数据源名称字符串
private static final String ORACLE_DATASOURCE_STRING="java:comp/env/users";
//使用单例模式来保存数据库对象
private static ThreadLocal<Connection> tlconn=new ThreadLocal<Connection>();
/**
* 获取JNDI连接数据库对象
* @throws NamingException
* @throws SQLException
* */
public Connection getConnection() throws NamingException, SQLException{
/*
* ThreadLocal类可以保存一个对象,只能保存一个, 调用get方法得到保存的对象, 调用set方法向ThreadLocal中保存对象
* 调用remove()方法,将ThreadLocal中的对象移除
* 如果ThreadLocal中保存了连接对象,就从ThreadLocal中获取连接对象
* 如果ThreadLocal没有保存连接对象,就从连接池中获取一个连接对象,并把该连接对象保存到ThreadLocal中
*/
//从ThreadLocal获取Connection对象
Connection conn=tlconn.get();
//如果ThreadLocal中存在连接对象,则返回连接对象
if(conn!=null){
return conn;
}else{
//否则从连接池中获取一个连接对象,然后将连接对象放到ThreadLocal中,并返回该连接对象
//数据源DataSource由Tomcat提供,不能在程序中创建实例,并使用JNDI获得DataSource引用
/*
*JNDI(Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API
*javax.namming.Context接口的lookup()方法
*/
Context context=new InitialContext();
/*
*javax.sql.DataSource接口负责建立与数据库的连接
*从Tomcat的数据源获得连接
*把连接保存在连接池中
*/
//mysql
DataSource ds=(DataSource) context.lookup(MYSQL_DATASOURCE_STRING);
//sqlserver
// DataSource ds=(DataSource) context.lookup(SQLSERVER_DATASOURCE_STRING);
// //oracle
// DataSource ds=(DataSource) context.lookup(ORACLE_DATASOURCE_STRING);
//获取连接对象
conn=ds.getConnection();
//将数据库连接对象放入到ThreadLocal中
tlconn.set(conn);
return conn;
}
}
/**
* 从ThreadLocal中移除连接对象,以免线程池误操作本地变量
* */
public void removeConnection()throws Exception {
tlconn.remove();//将ThreadLocal中保存的连接对象移除,移除后tl.get()方法得到的结果是null
}
/**
* 为sql语句占位符赋值
* */
private void operateParameters(PreparedStatement pstmt,Object ... args) throws SQLException {
if (args != null && args.length > 0) {
for (int i = 0; i < args.length; i++) {
pstmt.setObject(i + 1, args[i]);
}
}
}
/**
* 将结果集转换为实体对象的接口
* Mapper接口用于规定将数据行转换为entity的方法,以Mapper结尾的类(如:ArticleMapper)是该接口的子类
* Mapper子类用于将rs指向的数据行转换为指定的entity对象
* */
public static interface RowMapper<T> {
public T rowMapper(ResultSet rs);
}
/**
* 数据库查询方法
* @return list<T>
* 第二个参数rowmapper:明确由哪个类负责将数据行转换为entity对象
* 第一个参数:sql语句
* 第三个参数:sql语句参数的值
* */
public <T> List<T> executeQuery(String sql, RowMapper mapper,Object ... values) throws Exception{
List list = new ArrayList<T>();
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
operateParameters(pstmt, values);
rs = pstmt.executeQuery();
//将结果集一条记录转换为实体对象,并添加到集合中
while (rs.next()) {
T obj = (T) mapper.rowMapper(rs);
list.add(obj);
}
} catch (SQLException e) {
e.printStackTrace();
throw new Exception("执行数据查询时,失败");
} finally {
//关闭rs
closeResultSet(rs);
//关闭pstmt
closeStatement(pstmt);
//注意,rs和pstmt是DBManager关闭的,conn是有调用DBManager类关闭的
}
return list;
}
/**
* 数据库更新数据方法(添加、修改、删除)
* @return int
* */
public int executeUpdate(String sql, Object...args) throws Exception {
PreparedStatement pstmt = null;
int rowCount = 0;
Connection conn = getConnection();
try {
pstmt = conn.prepareStatement(sql);
operateParameters(pstmt, args);
rowCount = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
throw new Exception("更新数据失败");
} finally {
closeStatement(pstmt);
}
return rowCount;
}
/**
* 执行聚合函数查询(求总和)
* */
public int executeScalar(String sql,Object ...values){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
operateParameters(pstmt, values);
rs = pstmt.executeQuery();
if(rs.next()){
return rs.getInt(1);
}
return 0;
}catch (Exception e) {
return 0;
}finally{
closeResultSet(rs);
closeStatement(pstmt);
}
}
/**
* 关闭连接
*
* @param dbConnection
* Connection
*/
public void closeConnection() {
try {
Connection conn = tlconn.get();
if (conn != null && (!conn.isClosed())) {
//关闭连接对象
conn.close();
conn = null;
//将连接对象从tl中移除
tlconn.remove();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 关闭结果集
*/
public void closeResultSet(ResultSet rs) {
try {
if (rs != null) {
rs.close();
rs = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 关闭语句
*/
public void closeStatement(PreparedStatement pstmt) {
try {
if (pstmt != null) {
pstmt.close();
pstmt = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 开始事务
* */
public void setAutoCommit()throws Exception {
try {
getConnection();
//开始事务
tlconn.get().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
throw new Exception("开始事务时,出现错误");
}
}
/**
* 提交事务
* */
public void commit() throws Exception {
try {
//提交事务
tlconn.get().commit();
} catch (SQLException e) {
e.printStackTrace();
throw new Exception("事务提交失败");
}
}
/**
* 回滚事务
* */
public void rollback() throws Exception{
try {
//回滚事务
tlconn.get().rollback();
} catch (SQLException e) {
e.printStackTrace();
throw new Exception("事务回滚失败");
}
}
}
二、将结果集rs指向的数据当前一行记录转换为指定的entity对象的mapper类(如:FoodMapper)
package com.myphome.mapper;
import java.sql.ResultSet;
import com.myphome.dao.JNDIDBManager;
import com.myphome.entity.Food;
/**
*FoodMapper用于将界面需要的数据查询出来,然后将rs指向的一条记录转换为Food实体类
*/
public class FoodMapper implements
JNDIDBManager.RowMapper<Food> {
public Food rowMapper(ResultSet rs) {
Food food = null;
try {
food = new Food();
food.setAgio(rs.getDouble("agio"));
food.setCharacteristics(rs.getString("characteristics"));
food.setCuisine(null);//把其他实体类作为此对象属性赋值(用第二个sql查询赋值,即sql嵌套)
food.setCuisineId(rs.getInt("cuisineId"));
food.setEatery(null);
food.setEateryId(rs.getInt("eateryId"));
food.setFingredient(rs.getString("fingredient"));
food.setId(rs.getInt("id"));
food.setImg(rs.getString("img"));
food.setIntegral(rs.getInt("integral"));
food.setName(rs.getString("name"));
food.setPrice(rs.getDouble("price"));
food.setRemark(rs.getString("remark"));
} catch (Exception e) {
food =null;
e.printStackTrace();
}
return food;
}
}
三、Food实体类
package com.myphome.entity;
import java.io.Serializable;
/**
* 菜品
*/
public class Food implements Serializable{
private int id; //食物的主键
private String name;//食物的名称
private String fingredient;//食物的配料
private double price;//食物的价格
private double agio;//食物的折扣
private String remark;//食物的简介
private String characteristics;//食物的特色
private String img;//食物的图片
private int integral;//购买食物所送积分
private int cuisineId;//食物所属菜系的id
private int eateryId;//食物所属餐馆的id
//对象属性,菜系类
private Cuisine cuisine;
//对象属性,菜馆类
private Eatery eatery;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 获取配料
* */
public String getFingredient() {
return fingredient;
}
/**
* 设置配料
* */
public void setFingredient(String fingredient) {
this.fingredient = fingredient;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public double getAgio() {
return agio;
}
public void setAgio(double agio) {
this.agio = agio;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
/**
* 获取特色
* */
public String getCharacteristics() {
return characteristics;
}
/**
* 设置特色
* */
public void setCharacteristics(String characteristics) {
this.characteristics = characteristics;
}
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
/**
* 获取积分
* */
public int getIntegral() {
return integral;
}
/**
* 设置积分
* */
public void setIntegral(int integral) {
this.integral = integral;
}
public int getCuisineId() {
return cuisineId;
}
public void setCuisineId(int cuisineId) {
this.cuisineId = cuisineId;
}
public int getEateryId() {
return eateryId;
}
public void setEateryId(int eateryId) {
this.eateryId = eateryId;
}
public Cuisine getCuisine() {
return cuisine;
}
public void setCuisine(Cuisine cuisine) {
this.cuisine = cuisine;
}
public Eatery getEatery() {
return eatery;
}
public void setEatery(Eatery eatery) {
this.eatery = eatery;
}
}
四、JNDI的配置步骤:
1.Tomcat的conf/context.xml中的配置:
<Context>
<!--**JNDI DataSource数据源tomcat下配置**-->
<!--sqlserver-->
<Resource name="googlesnack" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="sa" password="sa" driverClassName="com.microsoft.jdbc.sqlserver.SQLServerDriver"
url="jdbc:sqlserver://localhost:1433;DatabaseName=google_snack"/>
<!--mysql-->
<Resource name="jdbc/blog" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="60000"
username="root" password="root" driverClassName="org.gjt.mm.mysql.Driver"
url="jdbc:mysql://localhost:3306/blog"/>
<!--oracle-->
<Resource name="users" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="60000"
username="kekeai" password="kekeai123" driverClassName="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@localhost:1521:orcl"/>
</Context>
注:
2.加入数据库驱动文件:
把数据库驱动的.jar文件,加入到Tomcat下的lib中。
3.(可省略)应用程序的web.xml文件的配置,在web.xml中配置<resource-ref>:
注:如果用junit测试,将报错:
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
解决:用web项目形式测试,则成功!
//tomcat下context.xml 中有汉字将报编码错误。
//界面提交using POST method,则:this.dopost(request,response);