Files
2026-07-10 15:25:05 +08:00

644 lines
26 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright 2010 www.Zhaizj.Framework.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Zhaizj.Framework.Data;
using Zhaizj.Framework.Log;
using Zhaizj.Framework.ORM.Utils;
using Zhaizj.Framework.ORM.Operation;
namespace Zhaizj.Framework.ORM {
[Serializable]
internal class SqlBuilder {
private static readonly ILog logger = LogManager.GetLogger( typeof( SqlBuilder ) );
//-------------------------初始化-------------------------
private IList _propertyList;
private EntityInfo _entityInfo;
public SqlBuilder( Type type ) {
_entityInfo = Entity.GetInfo( type );
_propertyList = _entityInfo.SavedPropertyList;
}
public SqlBuilder( EntityInfo entityInfo ) {
_entityInfo = entityInfo;
_propertyList = _entityInfo.SavedPropertyList;
}
//-------------------------Sql语句-------------------------
public String GetFindConditionSql( String propertyString, String condition, Dictionary<String, Object> paramMap, int maxResults ) {
String[] arrConditonResult = getConditionResult( propertyString, condition, paramMap );
String sql = "";
if (maxResults > 0) {
sql = String.Format( "select {0} from {1}{2}", arrConditonResult[0], arrConditonResult[1], arrConditonResult[2] );
sql = _entityInfo.Dialect.GetLimit( sql, maxResults );
}
else {
sql = String.Format( "select {0} from {1}{2}", arrConditonResult[0], arrConditonResult[1], arrConditonResult[2] );
}
return sql;
}
public String GetFindById( int id, String propertyString ) {
String columnString = getColumnsByProperty( propertyString );
String sql = String.Format( "select {0} from {1} where Id={2}", columnString, _entityInfo.TableName, id );
logger.Info( String.Format( "{0}[{1}_FindById] {2}", LoggerUtil.SqlPrefix, _entityInfo.Name, sql ) );
return sql;
}
// TODO
public String GetFindById( String pkeyValue ) {
String pkeyName = ""; //_entityInfo.getPrimaryKeyName();
String sql = String.Format( "select * from {0} where {1}={2}", _entityInfo.TableName, pkeyName, pkeyValue );
logger.Info( String.Format( "{0}[{1}_FindById] {2}", LoggerUtil.SqlPrefix, _entityInfo.Name, sql ) );
return sql;
}
public String GetDeleteSql( String condition ) {
String where = condition.Trim().ToLower();
where = where.StartsWith( "where " ) ? where : "where "+ condition;
return string.Format( "delete from {0} {1}", _entityInfo.TableName, where );
}
public String GetCountSql( String condition ) {
String[] arrConditonResult = getConditionResult( "", condition, new Dictionary<String, Object>() );
return String.Format( "select count(*) from {0}{1}", arrConditonResult[1], arrConditonResult[2] );
}
public String GetPropertyList( String tbl, String selectColumn, String idlist ) {
String sql;
if (idlist.IndexOf( "," ) < 0) {
sql = "select " + selectColumn + " from " + tbl + " where Id=" + idlist;
}
else {
sql = "select " + selectColumn + " from " + tbl + " where Id in (" + idlist + ")";
}
logger.Info( LoggerUtil.SqlPrefix + "[" + _entityInfo.Name + "_FindPropertyList]" + sql );
return sql;
}
public String GetPageSql( PageCondition pc ) {
String columnString = getColumnsByProperty( pc.Property );
//------------------------ condition ------------------------------
String joinTable = "";
pc.ConditionStr = processCondition( pc.ConditionStr, new Dictionary<String, Object>(), ref joinTable );
Boolean isJoin = false;
if (strUtil.HasText( joinTable )) {
isJoin = true;
joinTable = _entityInfo.TableName + " t0, " + joinTable;
// columnString中的每个columnName都要加上t0前缀
columnString = addJoinTablePrefix( columnString, ',' );
}
else {
joinTable = _entityInfo.TableName;
}
joinTable = joinTable.Trim().TrimEnd( ',' );
if (strUtil.HasText( pc.ConditionStr )) {
if (pc.ConditionStr.ToLower().Trim().StartsWith( "and " )) {
//"and id=7" 剔除and开头
pc.ConditionStr = " where " + pc.ConditionStr.Trim().Substring( 3 );
}
//"order by id" 加上一个空格
else if (pc.ConditionStr.Trim().ToLower().StartsWith( "order" )) {
pc.ConditionStr = " " + pc.ConditionStr;
}
else {
//加上 where
pc.ConditionStr = " where " + pc.ConditionStr;
}
}
//------------------------ order ------------------------------
Boolean isCustomerOrder = false;
if (pc.ConditionStr.ToLower().IndexOf( " order " ) >= 0) isCustomerOrder = true;
String orderString = "";
pc.OrderStr = pc.OrderStr.ToLower();
// 默认排序方式:逆序
if (isCustomerOrder == false) {
if (pc.OrderStr == "asc")
orderString = " order by Id asc";
else
orderString = " order by Id desc";
if (isJoin) orderString = orderString.Replace( "Id", "t0.Id" );
}
else {
// 自定义排序方式
String newOrderString = "";
String tempString = pc.ConditionStr;
//此处conditionString已经不带order
pc.ConditionStr = pc.ConditionStr.Substring( 0, pc.ConditionStr.ToLower().IndexOf( "order " ) );
orderString = tempString.Replace( pc.ConditionStr, "" );
String[] arrOrder = orderString.Split( new char[] { ' ', ',' } );
foreach (String s in arrOrder) {
if (s.ToLower() == "asc" || s.ToLower() == "desc") {
newOrderString += s + ", ";
}
else if (s.ToLower() != "order" && s.ToLower() != "by" && s != null && s != "") {
if (isJoin)
newOrderString += "t0." + s + " ";
else
newOrderString += s + " ";
}
else {
newOrderString += s + " ";
}
}
orderString = newOrderString.Trim().TrimEnd( ',' );
}
String _condition_nowhere = pc.ConditionStr.Trim();
if (strUtil.HasText( _condition_nowhere ) && _condition_nowhere.ToLower().StartsWith( "where" )) {
_condition_nowhere = pc.ConditionStr.Trim().Substring( 5 );
_condition_nowhere = "and " + _condition_nowhere;
}
String sql = "";
String pageCountSql = "select count(*) from " + joinTable + pc.ConditionStr;
//callback执行beginCount方法
pc.CurrentPage = pc.beginCount( pageCountSql, pc.Pager, _entityInfo );
// TODO:最后一页优化
if (pc.CurrentPage == 1) {
sql = "select " + columnString + " from " + joinTable + pc.ConditionStr + orderString;
sql = _entityInfo.Dialect.GetLimit( sql, pc.Size );
}
else {
StringBuilder sb = new StringBuilder();
getPageSql( pc, columnString, joinTable, isJoin, isCustomerOrder, orderString, _condition_nowhere, sb );
sql = sb.ToString();
}
logger.Info( LoggerUtil.SqlPrefix + "[Page Sql] " + sql );
return sql;
}
private void getPageSql( PageCondition pc, String columnString, String joinTable, Boolean isJoin, Boolean isCustomerOrder, String orderString, String _condition_nowhere, StringBuilder sb ) {
if (_entityInfo.DbType == DatabaseType.MySql) {
int startRow = pc.Size * (pc.CurrentPage - 1);
sb.Append( "select " );
sb.Append( columnString );
sb.Append( " from " );
sb.Append( joinTable );
sb.Append( " " );
sb.Append( pc.ConditionStr );
sb.Append( " " );
sb.Append( orderString );
sb.Append( " limit " );
sb.Append( startRow );
sb.Append( ", " );
sb.Append( pc.Size );
return;
}
if (isCustomerOrder == false) {
sb.Append( "select top " );
sb.Append( pc.Size );
sb.Append( " " );
sb.Append( columnString );
sb.Append( " from " );
sb.Append( joinTable );
if (pc.OrderStr == "asc") {
if (isJoin)
sb.Append( " where t0.Id>(select max(Id) from (select top " );
else
sb.Append( " where Id>(select max(Id) from (select top " );
}
else {
if (isJoin)
sb.Append( " where t0.Id<(select min(Id) from (select top " );
else
sb.Append( " where Id<(select min(Id) from (select top " );
}
sb.Append( pc.Size * (pc.CurrentPage - 1) );
if (isJoin)
sb.Append( " t0.Id from " );
else
sb.Append( " Id from " );
sb.Append( joinTable );
sb.Append( pc.ConditionStr );
sb.Append( orderString );
sb.Append( ") as t) " );
//sb.Append( "and " );
sb.Append( _condition_nowhere );
sb.Append( orderString );
}
else if (_entityInfo.DbType == DatabaseType.Access || _entityInfo.DbType == DatabaseType.SqlServer2000) {
sb.Append( "select top " );
sb.Append( pc.Size );
sb.Append( " " );
sb.Append( columnString );
sb.Append( " from " );
sb.Append( joinTable );
if (strUtil.IsNullOrEmpty( pc.ConditionStr )) {
if (isJoin)
sb.Append( " where t0.Id not in ( select top " );
else
sb.Append( " where Id not in ( select top " );
}
else {
sb.Append( pc.ConditionStr );
if (isJoin)
sb.Append( " and t0.Id not in ( select top " );
else
sb.Append( " and Id not in ( select top " );
}
sb.Append( pc.Size * (pc.CurrentPage - 1) );
if (isJoin)
sb.Append( " t0.Id from " );
else
sb.Append( " Id from " );
sb.Append( joinTable );
sb.Append( pc.ConditionStr );
sb.Append( orderString );
sb.Append( " ) " );
//sb.Append( "and " );
sb.Append( _condition_nowhere );
sb.Append( " " );
sb.Append( orderString );
}
else {
int startRow = pc.Size * (pc.CurrentPage - 1);
int endRow = startRow + pc.Size;
sb.AppendFormat( "select {0} from ", columnString );
sb.Append( "(" );
sb.AppendFormat( "select ROW_NUMBER() over ( {0} ) as rowNumber, {1} from {2} {3}", orderString, columnString, joinTable, pc.ConditionStr );
sb.Append( ") as tmpTable " );
sb.AppendFormat( "where (rowNumber between {0} and {1}) {2}", (startRow + 1), endRow, _condition_nowhere );
}
}
//-------------------------帮助方法-------------------------
private String[] getConditionResult( String propertyString, String condition, Dictionary<String, Object> paramMap ) {
String columnString = getColumnsByProperty( propertyString );
String joinTable = "";
String conditionString = condition;
if (strUtil.HasText( conditionString )) {
conditionString = processCondition( conditionString, paramMap, ref joinTable ).Trim();
}
if (strUtil.HasText( joinTable )) {
joinTable = _entityInfo.TableName + " t0, " + joinTable;
// columnString中的每个columnName都要加上t0前缀
columnString = addJoinTablePrefix( columnString, ',' );
}
else {
joinTable = _entityInfo.TableName;
}
joinTable = joinTable.Trim().TrimEnd( ',' );
if (strUtil.HasText( conditionString )) {
if (conditionString.ToLower().StartsWith( "and" )) {
//"and id=7" 剔除and开头
conditionString = " where " + conditionString.Substring( 3 ).Trim();
}
//"order by id" 加上一个空格
else if (conditionString.ToLower().StartsWith( "order" )) {
conditionString = " " + conditionString.Trim();
}
else {
//加上 where
conditionString = " where " + conditionString.Trim();
}
}
return new String[] { columnString, joinTable, conditionString };
}
// 示例:Id,Title,Member.Id,Cat.Name
public String getColumnsByProperty( String propertyString ) {
if (!strUtil.HasText( propertyString ) || propertyString.Trim().Equals( "*" )) return "*";
//将 propertyString 与 当前实体的所有属性比较,得到 columnList
String[] arrProperty = propertyString.Split( ',' );
String result = "";
foreach (EntityPropertyInfo ep in _propertyList) {
foreach (String pString in arrProperty) {
if (isAddColumn( pString.Trim(), ep )) {
//实体属性的数据,这里不联表查询。而是获取相应id,通过id in(idlist) 得到数据再填充
result += ep.ColumnName + ",";
break;
}
}
}
return result.Trim().TrimEnd( ',' );
}
// 是否在sql中选择当前属性的column
private Boolean isAddColumn( String selectedProperty, EntityPropertyInfo ep ) {
//选择字符串和属性完全同名
if (selectedProperty.Equals( ep.Name )) return true;
//属性Member,selectProperty为Member.Name,只要前缀相同,即可选取属性的columnName
if (ep.IsEntity && selectedProperty.StartsWith( ep.Name + "." )) return true;
return false;
}
// 示例:Member.Id=6 and Cat.Id>7
// art.Find("Author=:author and Title=:title and Id>:id order by Id desc")
public String processCondition( String conditionString, Dictionary<String, Object> paramMap, ref String joinTable ) {
//logger.Info( "conditionString=>" + conditionString );
// 根据不同方言,替换参数,比如“Author=:author and Title=:title”需要替换成“Author=? and Title=?”
foreach (String key in paramMap.Keys) {
conditionString = conditionString.Replace( ":" + key, _entityInfo.Dialect.GetParameter( key ) );
}
//如果是实体属性在设置条件,还要负责把 Member.Id=:mid 转换成 TMemberId=?:不联表查询
// 1、当前实体具有的column _entityInfo.ColumnList
// 2、找到参数查询所对应的column
if (isConditionHasEntity( conditionString )) {
//是否联表查询
Boolean isJoinedQuery = false;
String _join_condition = "";
int _joined_Property_Count = 0;
// and (Member.Id=? and Cat.Name=?) order by Id desc
List<String[]> items = SqlUtil.getEntityProperties( conditionString );
foreach (String[] pair in items) {
String strItem = pair[0] + "." + pair[1];
if (!pair[1].ToLower().Equals( "id" )) isJoinedQuery = true;
if (!isJoinedQuery) {
conditionString = conditionString.Replace( strItem, _entityInfo.GetColumnName( pair[0] ) );
}
else {
_joined_Property_Count += 1;
EntityPropertyInfo _e_p = _entityInfo.GetProperty( pair[0] );
EntityPropertyInfo _sub_p = _e_p.EntityInfo.GetProperty( pair[1] );
String subColumn = (_sub_p == null ? pair[1] : _sub_p.ColumnName);
String _sub_column_name = "t" + _joined_Property_Count + "." + subColumn;
// joinTable
joinTable += _e_p.EntityInfo.TableName + " t" + _joined_Property_Count + ",";
// replace entity property
conditionString = conditionString.Replace( strItem, _sub_column_name );
_join_condition += "and t0." + _e_p.ColumnName + "=t" + _joined_Property_Count + ".Id ";
}
}
// add prefix "0" to all current entity property's column, especially in "order by id desc" etc.(=>order by t0.id desc)
if (isJoinedQuery) conditionString = addJoinTablePrefix( conditionString, ' ' );
//conditionString = _join_condition + conditionString;
conditionString = conditionString.Trim();
_join_condition = _join_condition.Trim();
if (conditionString.StartsWith( "and " ) == false && _join_condition.EndsWith( " and" ) == false) {
conditionString =_join_condition + " and " + conditionString;
}
else {
conditionString = _join_condition + " " + conditionString;
}
}
return conditionString;
}
// " and ( ControlPath = '~/Module/BlogMain.ascx' ) order by Id desc"
// " and Id>6 and Member.Name = 'zhang' "
// " and UnitPrice = 32.80 "
private Boolean isConditionHasEntity( String conditionString ) {
foreach (EntityPropertyInfo ep in this._entityInfo.EntityPropertyList) {
if (conditionString.IndexOf( " " + ep.Name + "." ) > -1) return true;
if (conditionString.StartsWith( ep.Name + "." )) return true;
if (conditionString.IndexOf( "(" + ep.Name + "." ) > -1) return true;
}
return false;
}
// 用于1)select columnNames
// 2)where 中,比如ReceiverId=1 and IsDelete=0
private String addJoinTablePrefix( String targetString, char seperator ) {
if (strUtil.IsNullOrEmpty( targetString )) return null;
String[] _arr_Str;
if (targetString.Equals( "*" )) {
_arr_Str = _entityInfo.ColumnList.Split( seperator );
}
else {
_arr_Str = targetString.Split( seperator );
}
String _result_string = "";
for (int i = 0; i < _arr_Str.Length; i++) {
String _str_item = _arr_Str[i].Trim(); //ReceiverId=1
if (!strUtil.HasText( _str_item )) continue;
if (_str_item.IndexOf( '=' ) > 0) {
String[] _arrItemPair = _str_item.Split( '=' );
_str_item = checkColumn( _arrItemPair[0] ) + "=" + _arrItemPair[1];
}
else {
_str_item = checkColumn( _str_item );
}
_result_string += _str_item + seperator;
}
return _result_string.TrimEnd( seperator ).Trim();
}
private String checkColumn( String orginalColumnName ) {
if (orginalColumnName.Equals( "=" ) ||
orginalColumnName.ToLower().Equals( "and" ) ||
orginalColumnName.ToLower().Equals( "or" ) ||
orginalColumnName.Equals( "?" ) ||
orginalColumnName.Equals( "(" ) ||
orginalColumnName.Equals( ")" ) ||
orginalColumnName.Equals( ">" ) ||
orginalColumnName.Equals( "<" ) ||
orginalColumnName.Equals( "order" ) ||
orginalColumnName.ToLower().Equals( "by" ) ||
orginalColumnName.ToLower().Equals( "asc" ) ||
orginalColumnName.ToLower().Equals( "desc" ) ||
orginalColumnName.Equals( "<>" ) ||
orginalColumnName.ToLower().Equals( "like" ))
return orginalColumnName;
if (orginalColumnName.IndexOf( "." ) > 0) return orginalColumnName;
foreach (EntityPropertyInfo ep in _propertyList) {
if (orginalColumnName.ToLower().Equals( ep.ColumnName.ToLower() )) {
return "t0." + orginalColumnName;
}
}
return orginalColumnName;
}
//String sql = "select Id,Name,ArticleCount from TCat";
//select top 1 Id,CategoryId,Name,FriendUrl,Gender,Pic,TemplateId from (note top 1)
public static String GetColumnString( String sql ) {
sql = sql.Trim();
int fromIndex = sql.ToLower().IndexOf( "from" );
sql = sql.Substring( 0, fromIndex ).Trim();
sql = sql.Substring( 7 ); // delete "select"
// TODO:database is mysql : don't use "top"
if (sql.ToLower().IndexOf( "top" ) > -1) {
String topOne = sql.Split( ',' )[0].Trim(); //=>top 1 Id
String[] arrTopOne = topOne.Split( ' ' );//=>["top","1","Id"]
String firstColumn = arrTopOne[arrTopOne.Length - 1];//=>Id
sql = sql.Replace( topOne, firstColumn );
}
sql = sql.Replace( " ", "" );
return sql;
}
//Id,Title,Member.Id,Cat.Name,Cat.ArticleCount,Board.Name
public static String GetIncludeProperty( String propertyString ) {
String[] _arr_property = propertyString.Trim().Split( ',' );
StringBuilder sb = new StringBuilder( "," );
foreach (String propertyItem in _arr_property) {
String p = propertyItem.Trim();
if (p.IndexOf( "." ) > 0) {
String propertyName = p.Split( '.' )[0];
if (sb.ToString().IndexOf( "," + propertyName + "," ) < 0) { //avoid repeat property
sb.Append( propertyName );
sb.Append( "," );
}
}
}
return sb.ToString().TrimStart( ',' ).TrimEnd( ',' );
}
// Id,Title,Member.Id,Cat.Name,Cat.ArticleCount,Board.Name
// => 3 hashtable
// Hashtable["Member"] = "Id", Hashtable["Cat"]="Name,ArticleCount", Hashtable["Board"]="Name"
public static Hashtable GetIncludeSelectColumn( String propertyString, IList entityPropertyList ) {
Hashtable result = new Hashtable();
if (propertyString.Equals( "*" )) return result;
String[] _arr_property = propertyString.Trim().Split( ',' );
foreach (String propertyItem in _arr_property) {
String p = propertyItem.Trim();
if (p.IndexOf( "." ) < 0) continue;
String[] _arr_p_item = p.Split( '.' ); //Cat.ArticleCount
String propertyName = _arr_p_item[0]; //Cat
String subPropertyName = _arr_p_item[1]; //ArticleCount
if (result[propertyName] == null) { //first time fill column
result[propertyName] = getColumnNameFromPropertyList( entityPropertyList, propertyName, subPropertyName ) + ",";
}
else {
result[propertyName] = result[propertyName].ToString() + getColumnNameFromPropertyList( entityPropertyList, propertyName, subPropertyName ) + ",";
}
}
return result;
}
private static String getColumnNameFromPropertyList( IList entityPropertyList, String propertyName, String subPropertyName ) {
foreach (EntityPropertyInfo p in entityPropertyList) {
if (p.Name.Equals( propertyName )) {
return p.EntityInfo.GetProperty( subPropertyName ).ColumnName;
}
}
return null;
}
}
}