/****************************************************************************** * Product: Adempiere ERP & CRM Smart Business Solution * * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * * under the terms version 2 of the GNU General Public License as published * * by the Free Software Foundation. This program is distributed in the hope * * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., * * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * For the text or an alternative of this public license, you may reach us * * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * * or via info@compiere.org or http://www.compiere.org/license.html * *****************************************************************************/ package org.compiere.model; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.math.BigDecimal; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.logging.Level; import org.compiere.util.CCache; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.DisplayType; import org.compiere.util.Env; /** * Persistent Object Info. * Provides structural information. * * @author Jorg Janke * @version $Id: POInfo.java,v 1.2 2006/07/30 00:58:37 jjanke Exp $ * @author Victor Perez, e-Evolution SC *
  • [ 2195894 ] Improve performance in PO engine *
  • https://sourceforge.net/p/adempiere/feature-requests/555/ */ public class POInfo implements Serializable { /** * generated serial id */ private static final long serialVersionUID = -6346988499971159874L; /** * POInfo Factory Method * @param ctx context * @param AD_Table_ID AD_Table_ID * @return POInfo */ public static POInfo getPOInfo (Properties ctx, int AD_Table_ID) { return getPOInfo(ctx, AD_Table_ID, null); } /** * POInfo Factory Method * @param ctx context * @param AD_Table_ID AD_Table_ID * @param trxName Transaction name * @return POInfo instance */ public static synchronized POInfo getPOInfo (Properties ctx, int AD_Table_ID, String trxName) { Integer key = Integer.valueOf(AD_Table_ID); POInfo retValue = (POInfo)s_cache.get(key); if (retValue == null) { retValue = new POInfo(ctx, AD_Table_ID, false, trxName); if (retValue.getColumnCount() == 0) // May be run before Language verification retValue = new POInfo(ctx, AD_Table_ID, true, trxName); else s_cache.put(key, retValue); } return retValue; } // getPOInfo /** Cache of POInfo */ private static CCache s_cache = new CCache(I_AD_Table.Table_Name, "POInfo", 200, 0, false, 0); /** * Create Persistent Info * @param ctx context * @param AD_Table_ID AD_ Table_ID * @param baseLanguageOnly get in base language */ private POInfo (Properties ctx, int AD_Table_ID, boolean baseLanguageOnly) { this(ctx, AD_Table_ID, baseLanguageOnly, null); } /** * Create Persistent Info * @param ctx context * @param AD_Table_ID AD_ Table_ID * @param baseLanguageOnly get in base language * @param trxName transaction name */ private POInfo (Properties ctx, int AD_Table_ID, boolean baseLanguageOnly, String trxName) { m_ctx = ctx; m_AD_Table_ID = AD_Table_ID; boolean baseLanguage = baseLanguageOnly ? true : Env.isBaseLanguage(m_ctx, "AD_Table"); loadInfo (baseLanguage, trxName); } // PInfo /** Context */ private transient Properties m_ctx = null; /** Table_ID */ private int m_AD_Table_ID = 0; /** Table Name */ private String m_TableName = null; /** Access Level */ private String m_AccessLevel = MTable.ACCESSLEVEL_Organization; /** Columns */ private POInfoColumn[] m_columns = null; /** Table has Key Column */ private boolean m_hasKeyColumn = false; /** Table needs keep log*/ private boolean m_IsChangeLog = false; /** column name to index map **/ private Map m_columnNameMap; /** ad_column_id to index map **/ private Map m_columnIdMap; private Boolean m_IsTranslated = null; /** * Load Table and Column Info * @param baseLanguage true to load data in base language * @param trxName */ private void loadInfo (boolean baseLanguage, String trxName) { m_columnNameMap = new HashMap(); m_columnIdMap = new HashMap(); ArrayList list = new ArrayList(15); StringBuilder sql = new StringBuilder(); sql.append("SELECT t.TableName, c.ColumnName,c.AD_Reference_ID," // 1..3 + "c.IsMandatory,c.IsUpdateable,c.DefaultValue," // 4..6 + "e.Name,e.Description, c.AD_Column_ID, " // 7..9 + "c.IsKey,c.IsParent, " // 10..11 + "c.AD_Reference_Value_ID, vr.Code, " // 12..13 + "c.FieldLength, c.ValueMin, c.ValueMax, c.IsTranslated, " // 14..17 + "t.AccessLevel, c.ColumnSQL, c.IsEncrypted, " // 18..20 + "c.IsAllowLogging,c.IsAllowCopy,t.IsChangeLog "); // 21..23 sql.append("FROM AD_Table t" + " INNER JOIN AD_Column c ON (t.AD_Table_ID=c.AD_Table_ID)" + " LEFT OUTER JOIN AD_Val_Rule vr ON (c.AD_Val_Rule_ID=vr.AD_Val_Rule_ID)" + " INNER JOIN AD_Element"); if (!baseLanguage) sql.append("_Trl"); sql.append(" e " + " ON (c.AD_Element_ID=e.AD_Element_ID) " + "WHERE t.AD_Table_ID=?" + " AND c.IsActive='Y'"); if (!baseLanguage) sql.append(" AND e.AD_Language='").append(Env.getAD_Language(m_ctx)).append("'"); sql.append(" ORDER BY c.AD_Column_ID"); // PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement(sql.toString(), trxName); pstmt.setInt(1, m_AD_Table_ID); rs = pstmt.executeQuery(); while (rs.next()) { if (m_TableName == null) m_TableName = rs.getString(1); String ColumnName = rs.getString(2); int AD_Reference_ID = rs.getInt(3); boolean IsMandatory = "Y".equals(rs.getString(4)); boolean IsUpdateable = "Y".equals(rs.getString(5)); String DefaultLogic = rs.getString(6); String Name = rs.getString(7); String Description = rs.getString(8); int AD_Column_ID = rs.getInt(9); boolean IsKey = "Y".equals(rs.getString(10)); if (IsKey) m_hasKeyColumn = true; boolean IsParent = "Y".equals(rs.getString(11)); int AD_Reference_Value_ID = rs.getInt(12); String ValidationCode = rs.getString(13); int FieldLength = rs.getInt(14); String ValueMin = rs.getString(15); String ValueMax = rs.getString(16); boolean IsTranslated = "Y".equals(rs.getString(17)); // m_AccessLevel = rs.getString(18); String ColumnSQL = rs.getString(19); if (ColumnSQL != null && ColumnSQL.length() > 0 && (ColumnSQL.startsWith(MColumn.VIRTUAL_UI_COLUMN_PREFIX) || ColumnSQL.startsWith(MColumn.VIRTUAL_SEARCH_COLUMN_PREFIX))) ColumnSQL = "NULL"; if (ColumnSQL != null && ColumnSQL.contains("@")) ColumnSQL = Env.parseContext(Env.getCtx(), -1, ColumnSQL, false, true); boolean IsEncrypted = "Y".equals(rs.getString(20)); boolean IsAllowLogging = "Y".equals(rs.getString(21)); boolean IsAllowCopy = "Y".equals(rs.getString(22)); m_IsChangeLog="Y".equals(rs.getString(23)); POInfoColumn col = new POInfoColumn ( AD_Column_ID, ColumnName, ColumnSQL, AD_Reference_ID, IsMandatory, IsUpdateable, DefaultLogic, Name, Description, IsKey, IsParent, AD_Reference_Value_ID, ValidationCode, FieldLength, ValueMin, ValueMax, IsTranslated, IsEncrypted, IsAllowLogging, IsAllowCopy); list.add(col); m_columnNameMap.put(ColumnName.toUpperCase(), list.size() - 1); m_columnIdMap.put(AD_Column_ID, list.size() - 1); } } catch (SQLException e) { CLogger.get().log(Level.SEVERE, sql.toString(), e); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } // convert to array m_columns = new POInfoColumn[list.size()]; list.toArray(m_columns); } // loadInfo /** * String representation * @return String Representation */ @Override public String toString() { return "POInfo[" + getTableName() + ",AD_Table_ID=" + getAD_Table_ID() + "]"; } // toString /** * String representation for column * @param index column index * @return String Representation */ public String toString (int index) { if (index < 0 || index >= m_columns.length) return "POInfo[" + getTableName() + "-(InvalidColumnIndex=" + index + ")]"; return "POInfo[" + getTableName() + "-" + m_columns[index].toString() + "]"; } // toString /** * Get Table Name * @return Table Name */ public String getTableName() { return m_TableName; } // getTableName /** * Get AD_Table_ID * @return AD_Table_ID */ public int getAD_Table_ID() { return m_AD_Table_ID; } // getAD_Table_ID /** * Table has a Key Column * @return true if table has a key column */ public boolean hasKeyColumn() { return m_hasKeyColumn; } // hasKeyColumn /** * Get Table Access Level * @return Table.ACCESS.. */ public String getAccessLevel() { return m_AccessLevel; } // getAccessLevel /** * Get ColumnCount * @return column count */ public int getColumnCount() { return m_columns.length; } // getColumnCount /** * Get Column Index * @param ColumnName column name * @return index of column with ColumnName or -1 if not found */ public int getColumnIndex (String ColumnName) { Integer i = m_columnNameMap.get(ColumnName.toUpperCase()); if (i != null) return i.intValue(); return -1; } // getColumnIndex /** * Get Column Index * @param AD_Column_ID column * @return index of column with AD_Column_ID or -1 if not found */ public int getColumnIndex (int AD_Column_ID) { Integer i = m_columnIdMap.get(AD_Column_ID); if (i != null) return i.intValue(); return -1; } // getColumnIndex /** * Get AD_Column_ID * @param columnName * @return AD_Column_ID if found, -1 if not found */ public int getAD_Column_ID(String columnName) { for (int i = 0; i < m_columns.length; i++) { if (columnName.equalsIgnoreCase(m_columns[i].ColumnName)) // globalqss : modified to compare ignoring case [ 1619179 ] return m_columns[i].AD_Column_ID; } return -1; } /** * Get Column Info * @param index column index * @return column info */ protected POInfoColumn getColumn (int index) { if (index < 0 || index >= m_columns.length) return null; return m_columns[index]; } // getColumn /** * Get Column Name * @param index column index * @return column name */ public String getColumnName (int index) { if (index < 0 || index >= m_columns.length) return null; return m_columns[index].ColumnName; } // getColumnName /** * Get Column SQL or Column Name * @param index column index * @return column sql or column name */ public String getColumnSQL (int index) { if (index < 0 || index >= m_columns.length) return null; if (m_columns[index].ColumnSQL != null && m_columns[index].ColumnSQL.length() > 0) { if (m_columns[index].ColumnSQL.startsWith(MColumn.VIRTUAL_UI_COLUMN_PREFIX) || m_columns[index].ColumnSQL.startsWith(MColumn.VIRTUAL_SEARCH_COLUMN_PREFIX)) return "NULL AS " + m_columns[index].ColumnName; return m_columns[index].ColumnSQL + " AS " + m_columns[index].ColumnName; } return m_columns[index].ColumnName; } // getColumnSQL /** * Is Column Virtual? * @param index column index * @return true if column is virtual */ public boolean isVirtualColumn (int index) { if (index < 0 || index >= m_columns.length) return true; return m_columns[index].ColumnSQL != null && m_columns[index].ColumnSQL.length() > 0; } // isVirtualColumn /** * Is Column Virtual DB? * @param index column index * @return true if column is virtual DB */ public boolean isVirtualDBColumn (int index) { if (index < 0 || index >= m_columns.length) return true; return m_columns[index].ColumnSQL != null && m_columns[index].ColumnSQL.length() > 0 && !m_columns[index].ColumnSQL.startsWith(MColumn.VIRTUAL_UI_COLUMN_PREFIX) && !m_columns[index].ColumnSQL.startsWith(MColumn.VIRTUAL_SEARCH_COLUMN_PREFIX); } // isVirtualDBColumn /** * Is Column Virtual UI? * @param index index * @return true if column is virtual UI */ public boolean isVirtualUIColumn (int index) { if (index < 0 || index >= m_columns.length) return true; return m_columns[index].ColumnSQL != null && m_columns[index].ColumnSQL.length() > 0 && m_columns[index].ColumnSQL.startsWith(MColumn.VIRTUAL_UI_COLUMN_PREFIX); } // isVirtualUIColumn /** * Is Column Virtual Search? * @param index index * @return true if column is virtual search */ public boolean isVirtualSearchColumn (int index) { if (index < 0 || index >= m_columns.length) return true; return m_columns[index].ColumnSQL != null && m_columns[index].ColumnSQL.length() > 0 && m_columns[index].ColumnSQL.startsWith(MColumn.VIRTUAL_SEARCH_COLUMN_PREFIX); } // isVirtualSearchColumn /** * Get Column Label * @param index column index * @return column label */ public String getColumnLabel (int index) { if (index < 0 || index >= m_columns.length) return null; return m_columns[index].ColumnLabel; } // getColumnLabel /** * Get Column Description * @param index column index * @return column description */ public String getColumnDescription (int index) { if (index < 0 || index >= m_columns.length) return null; return m_columns[index].ColumnDescription; } // getColumnDescription /** * Get Column Class * @param index column index * @return Class */ public Class getColumnClass (int index) { if (index < 0 || index >= m_columns.length) return null; return m_columns[index].ColumnClass; } // getColumnClass /** * Get Column Display Type * @param index column index * @return DisplayType */ public int getColumnDisplayType (int index) { if (index < 0 || index >= m_columns.length) return DisplayType.String; return m_columns[index].DisplayType; } // getColumnDisplayType /** * Get Column Default Logic * @param index column index * @return Default Logic */ public String getDefaultLogic (int index) { if (index < 0 || index >= m_columns.length) return null; return m_columns[index].DefaultLogic; } // getDefaultLogic /** * Is Column Mandatory * @param index column index * @return true if column is mandatory */ public boolean isColumnMandatory (int index) { if (index < 0 || index >= m_columns.length) return false; return m_columns[index].IsMandatory; } // isMandatory /** * Is Column Updateable * @param index column index * @return true if column is updateable */ public boolean isColumnUpdateable (int index) { if (index < 0 || index >= m_columns.length) return false; return m_columns[index].IsUpdateable; } // isUpdateable /** * Set Column Updateable * @param index column index * @param updateable */ public void setColumnUpdateable (int index, boolean updateable) { if (index < 0 || index >= m_columns.length) return; m_columns[index].IsUpdateable = updateable; } // setColumnUpdateable /** * Set all columns updateable * @param updateable */ public void setUpdateable (boolean updateable) { for (int i = 0; i < m_columns.length; i++) m_columns[i].IsUpdateable = updateable; } // setUpdateable /** * Is Lookup Column * @param index column index * @return true if it is a lookup column */ public boolean isColumnLookup (int index) { if (index < 0 || index >= m_columns.length) return false; return DisplayType.isLookup(m_columns[index].DisplayType); } // isColumnLookup /** * Get Lookup * @param index column index * @return Lookup or null */ public Lookup getColumnLookup (int index) { if (!isColumnLookup(index)) return null; // int WindowNo = 0; // List, Table, TableDir Lookup lookup = null; try { lookup = MLookupFactory.get (m_ctx, WindowNo, m_columns[index].AD_Column_ID, m_columns[index].DisplayType, Env.getLanguage(m_ctx), m_columns[index].ColumnName, m_columns[index].AD_Reference_Value_ID, m_columns[index].IsParent, m_columns[index].ValidationCode); } catch (Exception e) { lookup = null; // cannot create Lookup } return lookup; /** @todo other lookup types */ } // getColumnLookup /** * Is Column Key * @param index column index * @return true if column is a key column */ public boolean isKey (int index) { if (index < 0 || index >= m_columns.length) return false; return m_columns[index].IsKey; } // isKey /** * Is Column Parent * @param index column index * @return true if column is a Parent column */ public boolean isColumnParent (int index) { if (index < 0 || index >= m_columns.length) return false; return m_columns[index].IsParent; } // isColumnParent /** * Is Column Translated * @param index column index * @return true if column is translated */ public boolean isColumnTranslated (int index) { if (index < 0 || index >= m_columns.length) return false; return m_columns[index].IsTranslated; } // isColumnTranslated /** * Is Table Translated * @return true if table is translated */ public synchronized boolean isTranslated () { if (m_IsTranslated == null) { m_IsTranslated = Boolean.FALSE; for (int i = 0; i < m_columns.length; i++) { if (m_columns[i].IsTranslated) { m_IsTranslated = Boolean.TRUE; break; } } } return m_IsTranslated.booleanValue(); } // isTranslated /** * Is Column (data) Encrypted * @param index column index * @return true if column is encrypted */ public boolean isEncrypted (int index) { if (index < 0 || index >= m_columns.length) return false; return m_columns[index].IsEncrypted; } // isEncrypted /** * Is column secure * @param index column index * @return true if column is secure */ public boolean isSecure(int index) { if (index < 0 || index >= m_columns.length) return false; return MColumn.get(m_columns[index].AD_Column_ID).isSecure(); } /** * Is allowed logging on this column * * @param index column index * @return true if column is allowed to be logged */ public boolean isAllowLogging(int index) { if (index < 0 || index >= m_columns.length) return false; return m_columns[index].IsAllowLogging; } // isAllowLogging /** * Is allowed copying this column * * @param index column index * @return true if column is allowed to be copied */ public boolean isAllowCopy(int index) { if (index < 0 || index >= m_columns.length) return false; return m_columns[index].IsAllowCopy; } // isAllowCopy /** * Get Column FieldLength * @param index column index * @return field length or 0 */ public int getFieldLength (int index) { if (index < 0 || index >= m_columns.length) return 0; return m_columns[index].FieldLength; } // getFieldLength /** * Get Column FieldLength * @param columnName Column Name * @return field length or 0 */ public int getFieldLength (String columnName) { int index = getColumnIndex( columnName ); if (index >= 0) { return getFieldLength( index ); } return 0; } /** * Validate value * @param index column index * @param value value to validate * @return null if valid, otherwise error message */ public String validate (int index, Object value) { if (index < 0 || index >= m_columns.length) return "RangeError"; // Mandatory (i.e. not null if (m_columns[index].IsMandatory && value == null) { return "FillMandatory"; } if (value == null) return null; // Length ignored // if (m_columns[index].ValueMin != null) { BigDecimal value_BD = null; try { if (m_columns[index].ValueMin_BD != null) value_BD = new BigDecimal(value.toString()); } catch (Exception ex){} // Both are Numeric if (m_columns[index].ValueMin_BD != null && value_BD != null) { // error: 1 - 0 => 1 - OK: 1 - 1 => 0 & 1 - 10 => -1 int comp = m_columns[index].ValueMin_BD.compareTo(value_BD); if (comp > 0) { return "LessThanMinValue"+";"+m_columns[index].ValueMin_BD.toPlainString(); } } else if (value instanceof Timestamp && m_columns[index].ValueMin_TS != null) // Date { if (((Timestamp) value).before(m_columns[index].ValueMin_TS)) { return "LessThanMinValue"+";"+m_columns[index].ValueMin; } } else // String { int comp = m_columns[index].ValueMin.compareTo(value.toString()); if (comp > 0) { return "LessThanMinValue"+";"+m_columns[index].ValueMin; } } } if (m_columns[index].ValueMax != null) { BigDecimal value_BD = null; try { if (m_columns[index].ValueMax_BD != null) value_BD = new BigDecimal(value.toString()); } catch (Exception ex){} // Both are Numeric if (m_columns[index].ValueMax_BD != null && value_BD != null) { // error 12 - 20 => -1 - OK: 12 - 12 => 0 & 12 - 10 => 1 int comp = m_columns[index].ValueMax_BD.compareTo(value_BD); if (comp < 0) { return "MoreThanMaxValue"+";"+m_columns[index].ValueMax_BD.toPlainString(); } } else if (value instanceof Timestamp && m_columns[index].ValueMax_TS != null) // Date { if (((Timestamp) value).after(m_columns[index].ValueMax_TS)) { return "MoreThanMaxValue"+";"+m_columns[index].ValueMax; } } else // String { int comp = m_columns[index].ValueMax.compareTo(value.toString()); if (comp < 0) { return "MoreThanMaxValue"+";"+m_columns[index].ValueMax; } } } return null; } // validate /** * Build SQL SELECT statement. * @return {@link StringBuilder} instance with the SQL statement. */ public StringBuilder buildSelect() { return buildSelect(false, false); } /** * Build SQL SELECT statement. * @param fullyQualified prefix column names with the table name * @param noVirtualColumn Include (false value) all declared virtual columns at once * or use lazy loading (true value). * @return {@link StringBuilder} instance with the SQL statement. */ public StringBuilder buildSelect(boolean fullyQualified, boolean noVirtualColumn) { return buildSelect(fullyQualified, noVirtualColumn ? new String[] {} : null); } /** * Build SQL SELECT statement. * @param fullyQualified prefix column names with the table name * @param virtualColumns names of virtual columns to include along with the regular table columns.
    * - if virtualColumns is null then all declared virtual columns will be included.
    * - if virtualColumns is an empty string array (new String[] {}), no declared virtual columns will be included. * @return {@link StringBuilder} instance with the SQL statement. */ public StringBuilder buildSelect(boolean fullyQualified, String ... virtualColumns) { StringBuilder sql = new StringBuilder("SELECT "); int size = getColumnCount(); int count = 0; for (int i = 0; i < size; i++) { boolean virtual = isVirtualColumn(i); if (virtual && virtualColumns != null) { boolean found = false; for(String virtualColumn : virtualColumns) { if(m_columns[i].ColumnName.equalsIgnoreCase(virtualColumn)) { found = true; break; } } if(!found) continue; } count++; if (count > 1) sql.append(","); String columnSQL = getColumnSQL(i); if (!virtual) columnSQL = DB.getDatabase().quoteColumnName(columnSQL); if (fullyQualified && !virtual) sql.append(getTableName()).append("."); sql.append(columnSQL); // Normal and Virtual Column if (fullyQualified && !virtual) sql.append(" AS ").append(m_columns[i].ColumnName); } sql.append(" FROM ").append(getTableName()); return sql; } /** * Is column should always be loaded for partial loading of PO * @param columnIndex * @return true if column should always be loaded for partial loading of PO */ protected boolean isColumnAlwaysLoadedForPartialPO(int columnIndex) { String columnName = getColumnName(columnIndex); boolean isKey = isKey(columnIndex); boolean isUUID = columnName.equals(PO.getUUIDColumnName(m_TableName)); // Always load key, uuid and standard columns if (isKey || isUUID || columnName.equalsIgnoreCase("ad_client_id") || columnName.equalsIgnoreCase("ad_org_id") || columnName.equalsIgnoreCase("isactive") || columnName.equalsIgnoreCase("created") || columnName.equalsIgnoreCase("createdby") || columnName.equalsIgnoreCase("updated") || columnName.equalsIgnoreCase("updatedby")) return true; else return false; } /** * Build SQL SELECT statement for columns. * @param fullyQualified prefix column names with the table name * @return {@link StringBuilder} instance with the SQL statement. */ public StringBuilder buildSelectForColumns(boolean fullyQualified, String[] columns) { StringBuilder sql = new StringBuilder("SELECT "); int size = getColumnCount(); int count = 0; for (int i = 0; i < size; i++) { String columnName = getColumnName(i); boolean virtual = isVirtualColumn(i); if (!isColumnAlwaysLoadedForPartialPO(i)) { Optional optional = Arrays.stream(columns).filter(e -> e.equalsIgnoreCase(columnName)).findFirst(); if (!optional.isPresent()) continue; } count++; if (count > 1) sql.append(","); String columnSQL = getColumnSQL(i); if (!virtual) columnSQL = DB.getDatabase().quoteColumnName(columnSQL); if (fullyQualified && !virtual) sql.append(getTableName()).append("."); sql.append(columnSQL); // Normal and Virtual Column if (fullyQualified && !virtual) sql.append(" AS ").append(m_columns[i].ColumnName); } sql.append(" FROM ").append(getTableName()); return sql; } /** * Is save changes to change log table * @return if table save change log */ public boolean isChangeLog() { return m_IsChangeLog; } /** * Read object from ois (for serialization) * @param ois * @throws ClassNotFoundException * @throws IOException */ private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { // default deserialization ois.defaultReadObject(); m_ctx = Env.getCtx(); } } // POInfo