java防sql注入的sql语句拼接工具sqlHandle

SQL拼接工具
介绍了一种用于构建灵活SQL查询的工具,该工具通过拼接SQL片段并使用参数化查询来提高安全性与灵活性。

     我在做网站的时候有一个需要 在不同的插入时间改变查询的逻辑结构,这个时候用PreparedStatement就不太适合了灵活性太差。所以我就写了一个sql拼接工具。它的原理是将sql语句段与值绑定在一起,然后在最后拼接的时候把值按顺序传人给PreparedStatement。这样我们任意拼接我们的sql语句还可以防止sql注入的困扰。

   平时我都是用Hibernate进行操作,但有时我多表查询是Hibernate的灵活性就也受到了限制,所以sqlHandle还是适用不少情况的。

  我已经将代码上传到osc代码托管服务了http://git.oschina.net/plug/sqlHandle 欢迎有兴趣的同学可以加入进来来优化一下。

package com.sql.db;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
/**
 * sql拼接代码
 * 一个完整的sql语句可以有多个sqlHandle拼接而成
 * sqlHandle分sql语句 与 sql值,在sql语句中值由?
 * 替代在最后运行是值将依此赋值给hibernate
 * @author 梁前武 
 * www.apkplug.com
 */
public class sqlHandle {
	public String strSql="%s";
	public Object fsql="";
	public String type="";
	public Object[] v=null;
	public List<sqlHandle> list=null;
	public sqlHandle(String strSql,Object fsql,String type,Object[] v){
		this.strSql=strSql;
		this.fsql=fsql;
		this.type=type;
		this.v=v;
	}
	public sqlHandle(Object fsql,String type,Object[] v){
		this.fsql=fsql;
		this.type=type;
		this.v=v;
	}
	public sqlHandle(){
	}
	public  SQLQuery handle(Session session){
		//拼接本sqlHandle sql字符串
		String sql=addWhereSql(strSql,fsql,type);
		for (int i=0;i<list.size();i++) {
			//依此拼接子sqlHandle
			sqlHandle sqlHandle = (sqlHandle) list.get(i);
			sql+=addWhereSql(sqlHandle.strSql,sqlHandle.fsql,sqlHandle.type);
		}
		System.out.println(sql);
		SQLQuery query=session.createSQLQuery(sql);
		//赋值
		setV(query,0);
		return query;
	}
	/**
	 * 将sql中?代替的值依此赋值给hibernate
	 * @param q
	 * @param i  传递值索引位置
	 * @return
	 */
	public int  setV(SQLQuery q,int i){
		if(v!=null)
		for(int j=0;j<v.length;j++){
			if(v[j]!=null){
				//System.out.println("添加参数");
				setV(q,i,v[j]);
				i++;
			}
		}
		if(fsql!=null&&fsql instanceof sqlHandle)
			i=((sqlHandle)fsql).setV(q, i);
		if(list!=null)
		for (int j=0;j<list.size();j++) {
			sqlHandle sqlHandle = (sqlHandle) list.get(j);
			i=sqlHandle.setV(q, i);
		}
		return i;
	}
	/**
	 * 将sql中?代替的值依此赋值给hibernate
	 * @param q
	 * @param i   值索引位置
	 * @param v   值
	 */
	public  void setV(SQLQuery q,int i,Object v){
		if(v instanceof String){
			q.setString(i, (String)v);
		}else if(v instanceof Integer){
			q.setInteger(i, (Integer)v);
		}else if(v instanceof Float){
			q.setFloat(i, (Float)v);
		}else if(v instanceof Double){
			q.setDouble(i, (Double)v);
		}else if(v instanceof Date){
			q.setDate(i, (Date)v);
		}else if(v instanceof java.sql.Date){
			q.setDate(i, (Date)v);
		}else if(v instanceof Boolean){
			q.setBoolean(i, (Boolean)v);
		}else if(v instanceof byte[]){
			q.setBinary(i, (byte[])v);
		}else if(v instanceof Byte){
			q.setByte(i, (Byte)v);
		}else if(v instanceof Calendar){
			q.setCalendar(i, (Calendar)v);
		}
	}
	
	/**
	 * 拼接sql字符串
	 * @param strSql 连接字符串 如 in(%s)
	 * @param sqlOne 实体字符串 如 select * user  可以是字符串也可以是sqlHandle
	 * @param type   连接类型   如 and,or
	 * @return 返回 如 and in(select * user)
	 */
	private static String addWhereSql(String strSql,Object sqlOne,String type){
		return  " "+type+" "+String.format(strSql, sqlOne)+" ";	
	}
	/**
	 * 添加多个sql参数值
	 * @param fsql  sql语句段   如 name=?
	 * @param type  sql连接类型  如 and
	 * @param v                 Object[]{"liming"} 如无值便为null
	 * @return
	 */
	public  sqlHandle AddMore(Object fsql,String type,Object[] v){
		if (list==null)
			list=new ArrayList<sqlHandle>();
		sqlHandle sql=new sqlHandle(fsql,type,v);
		list.add(sql);
		return this;
	}
	/**
	 * 添加0-1个sql参数
	 * @param fsql    sql语句段   如 name=?
	 * @param type    sql连接类型  如 and
	 * @param v       "liming" 如无值便为null
	 * @return
	 */
	public  sqlHandle Add(Object fsql,String type,Object v){
		AddMore( fsql, type,new Object[]{v});
		return this;
	}
	/**
	 * 添加多个sql参数值
	 * 多一共 strSql字符 有时我们需要 in(sql...) 这种情况。
	 * 我们可以通过设置 strSql=in(%s) fsql=sql... 这种形式来实现
	 * strSql默认为"%s"
	 * @param strSql  替代副
	 * @param fsql
	 * @param type
	 * @param v
	 * @return
	 */
	public  sqlHandle AddMoreAndRep(String strSql,Object fsql,String type,Object[] v){
		if (list==null)
			list=new ArrayList<sqlHandle>();
		sqlHandle sql=new sqlHandle(strSql,fsql,type,v);
		list.add(sql);
		return this;
	}
	/**
	 * 添加多个sql参数值
	 * 多一共 strSql字符 有时我们需要 in(sql...) 这种情况。
	 * 我们可以通过设置 strSql=in(%s) fsql=sql... 这种形式来实现
	 * strSql默认为"%s"
	 * @param strSql
	 * @param fsql
	 * @param type
	 * @param v
	 * @return
	 */
	public  sqlHandle AddAndRep(String strSql,Object fsql,String type,Object v){
		AddMoreAndRep(strSql, fsql, type,new Object[]{v});
		return this;
	}
	/**
	 * 生成自身sql语句
	 */
	public String toString(){
		String sql="";
		for (int i=0;i<list.size();i++) {
			sqlHandle sqlHandle = (sqlHandle) list.get(i);
			sql+=addWhereSql(sqlHandle.strSql,sqlHandle.fsql,sqlHandle.type);
		}
		return sql;
	}
}


01 package com.sql.db;
02 /**
03  * sql拼接测试代码
04  * @author 梁前武
05  * www.apkplug.com
06  */
07 public class test {
08  
09     /**
10      * @param args
11      */
12     public static void main(String[] args) {
13          //1
14          sqlHandle sql1=new sqlHandle();
15          sql1.Add("select * from user where " ,"",null).
16          Add("name=?","""liling").
17          Add("age=?","and"10);
18          System.out.println(sql1);  
19          //select * from user where    name=?  and age=?
20          sql1.AddMore("LIMIT ?,?"""new Object[]{1,2});
21          System.out.println(sql1);
22          //select * from user where    name=?  and age=?   LIMIT ?,?
23           
24          //2
25          sqlHandle sql2=new sqlHandle();
26          sql2.Add("select * from friend where " ,"",null).
27          Add("myfriend=?","""liling");
28          System.out.println(sql2);
29          // select * from friend where    myfriend=?
30          sql1.AddMoreAndRep(" in(%s) ",sql2, "and"null);
31          System.out.println(sql1);
32          //select * from user where    name=?  and age=?   LIMIT ?,?  and 
33          //in(  select * from friend where    myfriend=? ) 
34          //很明显sqlHandle添加时的顺序很关键
35           
36     }
37  
38 }

<think>好的,用户现在问的是如何在C++中获取SQL Server的数据并保存到字符串里。之前他们问过如何获取两个数据库表中重复的列数据,现在的问题可能是在同一个项目中的进一步需求,比如需要将查询结果处理后存储起来。 首先,我需要确定用户的使用场景。他们可能是在开发一个需要从SQL Server读取数据并进行处理的应用程序,比如生成报告、数据转换或者展示。保存到字符串可能是为了后续处理,比如输出到日志、序列化或者传输到其他系统。 接下来要考虑用户的需求。用户可能对C++操作SQL Server的流程不太熟悉,特别是如何连接数据库、执行查询以及处理结果集。他们可能需要一个具体的示例代码,同时希望了解每个步骤的细节和注意事项,确保代码能够正确运行。 用户可能没有明确提到的深层需求包括错误处理、资源管理和性能优化。比如,在连接数据库时如何处理异常,如何止内存泄漏,以及如何高效地处理大量数据。这些都是实际开发中常见的问题,需要一并考虑进去。 我需要选择合适的库来连接SQL Server。之前他们用的是MySQL Connector/C++,但这次是SQL Server,所以应该推荐ODBC或者Microsoft的官方库,比如Microsoft ODBC Driver for SQL Server或者Microsoft的OLE DB。ODBC是比较通用的选择,兼容性更好,所以示例代码中使用ODBC会比较合适。 然后,编写示例代码的步骤包括:连接数据库,执行查询,遍历结果集,将每一列数据转换为字符串,并保存起来。需要注意ODBC API的使用方法,比如分配句柄、连接字符串、执行SQL语句、绑定列内存等。此外,错误处理非常重要,每个ODBC函数调用后都应该检查返回码,确保操作成功。 用户可能对ODBC不太熟悉,所以需要详细解释每个步骤的作用,比如SQLAllocHandle、SQLDriverConnect、SQLExecDirect等函数的功能。同时,处理结果集时,如何获取每一行的数据,并将不同数据类型转换为字符串也是关键点。 另外,数据类型转换部分需要特别说明,比如如何处理整数、浮点数、日期时间等不同类型的数据,确保转换后的字符串格式正确。用户可能需要根据实际情况调整转换逻辑,特别是日期时间和二进制数据,可能需要特定的格式处理。 资源释放部分也是重点,比如释放句柄、断开连接,避免内存泄漏。需要提醒用户在代码中正确释放所有分配的资源,即使在发生异常的情况下也要确保资源被释放,可能需要使用try-catch块或者在析构函数中处理。 最后,注意事项部分需要涵盖连接字符串的格式、Unicode与ANSI字符的处理、大数据量的分页查询以及错误处理的细节。这些都能帮助用户在实际应用中避免常见的问题,提升代码的健壮性和效率。 总结来说,用户需要一个完整的示例,详细解释如何使用ODBC在C++中连接SQL Server,执行查询,并将结果保存到字符串中,同时需要覆盖错误处理、资源管理和数据类型转换等关键点,确保代码的可靠性和实用性。</think>在 C++ 中从 SQL Server 获取数据并保存到字符串中,通常需要以下步骤: **使用 ODBC(Open Database Connectivity)或 Microsoft 的 SQL Server 专用库(如 `Microsoft Data Access Components, MDAC`)**。以下是一个基于 ODBC 的详细实现方法: --- ### **核心步骤** 1. **配置 ODBC 数据源** 确保 SQL Server 的 ODBC 驱动已安装,并在系统数据源中配置连接(可选,也可直接通过连接字符串连接)。 2. **连接数据库** 使用 `sql.h` 和 `sqlext.h` 头文件中的 ODBC API 建立连接。 3. **执行 SQL 查询** 发送 SQL 语句(如 `SELECT`)到数据库。 4. **获取结果集并保存到字符串** 遍历结果集,逐行读取数据并转换为字符串。 --- ### **示例代码(使用 ODBC)** ```cpp #include <windows.h> #include <sql.h> #include <sqlext.h> #include <iostream> #include <string> #include <vector> // 错误处理函数 void checkODBCError(SQLHANDLE handle, SQLSMALLINT type, RETCODE retcode) { if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) return; SQLCHAR sqlstate[1024]; SQLCHAR message[1024]; SQLSMALLINT i = 1; SQLSMALLINT len; SQLRETURN rc; while ((rc = SQLGetDiagRec(type, handle, i, sqlstate, NULL, message, sizeof(message), &len)) == SQL_SUCCESS) { std::cerr << "ODBC Error: " << sqlstate << " - " << message << std::endl; i++; } } int main() { SQLHENV henv; SQLHDBC hdbc; SQLHSTMT hstmt; SQLRETURN retcode; // 1. 初始化 ODBC 环境 retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); checkODBCError(henv, SQL_HANDLE_ENV, retcode); retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0); checkODBCError(henv, SQL_HANDLE_ENV, retcode); // 2. 建立数据库连接 retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); checkODBCError(hdbc, SQL_HANDLE_DBC, retcode); // 连接字符串(根据实际情况修改) SQLCHAR connStr[] = "DRIVER={SQL Server};SERVER=localhost;DATABASE=YourDB;UID=sa;PWD=your_password;"; retcode = SQLDriverConnect(hdbc, NULL, connStr, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE); checkODBCError(hdbc, SQL_HANDLE_DBC, retcode); // 3. 执行查询 retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); checkODBCError(hstmt, SQL_HANDLE_STMT, retcode); SQLCHAR* sqlQuery = (SQLCHAR*)"SELECT column1, column2 FROM YourTable"; retcode = SQLExecDirect(hstmt, sqlQuery, SQL_NTS); checkODBCError(hstmt, SQL_HANDLE_STMT, retcode); // 4. 读取结果集并保存到字符串 std::vector<std::string> results; SQLCHAR columnData[256]; SQLLEN indicator; while (SQLFetch(hstmt) == SQL_SUCCESS) { std::string rowData; // 逐列读取数据(假设读取两列) for (int col = 1; col <= 2; col++) { retcode = SQLGetData(hstmt, col, SQL_C_CHAR, columnData, sizeof(columnData), &indicator); if (retcode == SQL_SUCCESS && indicator != SQL_NULL_DATA) { rowData += reinterpret_cast<char*>(columnData); rowData += " | "; // 列分隔符 } else { rowData += "NULL | "; } } results.push_back(rowData); std::cout << "行数据: " << rowData << std::endl; } // 5. 释放资源 SQLFreeHandle(SQL_HANDLE_STMT, hstmt); SQLDisconnect(hdbc); SQLFreeHandle(SQL_HANDLE_DBC, hdbc); SQLFreeHandle(SQL_HANDLE_ENV, henv); return 0; } ``` --- ### **关键函数说明** 1. **`SQLAllocHandle`** 分配 ODBC 环境、连接或语句句柄。 2. **`SQLDriverConnect`** 通过连接字符串建立到 SQL Server 的连接。 3. **`SQLExecDirect`** 执行 SQL 查询。 4. **`SQLFetch`** 逐行获取结果集数据。 5. **`SQLGetData`** 将当前行的某一列数据转换为字符串格式。 --- ### **注意事项** 1. **连接字符串格式** 根据实际服务器地址、数据库名称、账号密码修改 `connStr`。例如: ```cpp "DRIVER={SQL Server};SERVER=127.0.0.1;DATABASE=TestDB;UID=user;PWD=123456;" ``` 2. **数据类型转换** - `SQLGetData` 的第三个参数 `SQL_C_CHAR` 表示将数据转换为字符串。 - 若列数据类型为整数、日期等,需调整转换方式(如 `SQL_C_LONG` 或 `SQL_C_TIMESTAMP`)。 3. **处理 NULL 值** 检查 `indicator` 是否为 `SQL_NULL_DATA`,避免读取无效数据。 4. **性能优化** - 使用参数化查询(`SQLBindParameter`)避免 SQL 注入。 - 批量读取数据时,可设置游标类型提升效率。 5. **依赖项配置** - 编译时需链接 ODBC 库(Windows 默认包含,Linux 需安装 `unixODBC`)。 - 代码中需包含 `sql.h` 和 `sqlext.h` 头文件。 --- ### **扩展:保存到字符串的格式** 可以将结果保存为以下格式: ```cpp // 单行示例 std::string row = "John | 25 | 2023-10-01"; // JSON 格式 std::string json = R"({"name": "John", "age": 25})"; // CSV 格式 std::string csv = "John,25,2023-10-01\nAlice,30,2023-10-02"; ``` 根据需求选择合适的数据格式,并在 `SQLGetData` 后拼接字符串。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值