Attached Files | computedColumns.diff [^] (33,741 bytes) 2013-06-18 13:09 [Show Content] [Hide Content]diff -r e18b9062f750 modules/org.openbravo.service.json/src/org/openbravo/service/json/AdvancedQueryBuilder.java
--- a/modules/org.openbravo.service.json/src/org/openbravo/service/json/AdvancedQueryBuilder.java Wed Jun 12 12:00:11 2013 +0200
+++ b/modules/org.openbravo.service.json/src/org/openbravo/service/json/AdvancedQueryBuilder.java Tue Jun 18 13:09:15 2013 +0200
@@ -11,7 +11,7 @@
* under the License.
* The Original Code is Openbravo ERP.
* The Initial Developer of the Original Code is Openbravo SLU
- * All portions are Copyright (C) 2009-2012 Openbravo SLU
+ * All portions are Copyright (C) 2009-2013 Openbravo SLU
* All Rights Reserved.
* Contributor(s): ______________________________________.
************************************************************************
@@ -476,6 +476,12 @@
// or uses the display column to display that in the grid
Property useProperty = property;
String useFieldName = fieldName.replace(DalUtil.FIELDSEPARATOR, DalUtil.DOT);
+
+ if (useProperty.isComputedColumn()) {
+ // Computed columns are not directly accessed but through _computedColumns proxy
+ useFieldName = Entity.COMPUTED_COLUMNS_PROXY_PROPERTY + DalUtil.DOT + useFieldName;
+ }
+
if (properties.size() >= 2) {
final Property refProperty = properties.get(properties.size() - 2);
if (refProperty.getDomainType() instanceof TableDomainType) {
@@ -1195,6 +1201,13 @@
}
}
} else {
+ Entity searchEntity = getEntity();
+ Property property = searchEntity.getProperty(localOrderBy);
+ if (property != null && property.isComputedColumn()) {
+ // Computed columns are accessed through proxy
+ localOrderBy = Entity.COMPUTED_COLUMNS_PROXY_PROPERTY + DalUtil.DOT + localOrderBy;
+ }
+
paths.add(localOrderBy);
}
diff -r e18b9062f750 modules/org.openbravo.service.json/src/org/openbravo/service/json/JsonToDataConverter.java
--- a/modules/org.openbravo.service.json/src/org/openbravo/service/json/JsonToDataConverter.java Wed Jun 12 12:00:11 2013 +0200
+++ b/modules/org.openbravo.service.json/src/org/openbravo/service/json/JsonToDataConverter.java Tue Jun 18 13:09:15 2013 +0200
@@ -11,7 +11,7 @@
* under the License.
* The Original Code is Openbravo ERP.
* The Initial Developer of the Original Code is Openbravo SLU
- * All portions are Copyright (C) 2009-2011 Openbravo SLU
+ * All portions are Copyright (C) 2009-2013 Openbravo SLU
* All Rights Reserved.
* Contributor(s): ______________________________________.
************************************************************************
@@ -766,6 +766,7 @@
// do not change not updatable properties
// Updatable is a UI concept
// doNotHandleThisProperty |= !obObject.isNewOBObject() && !property.isUpdatable();
+ doNotHandleThisProperty |= property.isProxy();
return doNotHandleThisProperty;
}
diff -r e18b9062f750 src/org/openbravo/base/gen/GenerateEntitiesTask.java
--- a/src/org/openbravo/base/gen/GenerateEntitiesTask.java Wed Jun 12 12:00:11 2013 +0200
+++ b/src/org/openbravo/base/gen/GenerateEntitiesTask.java Tue Jun 18 13:09:15 2013 +0200
@@ -36,6 +36,7 @@
import org.openbravo.base.exception.OBException;
import org.openbravo.base.model.Entity;
import org.openbravo.base.model.ModelProvider;
+import org.openbravo.base.model.Property;
import org.openbravo.base.session.OBPropertiesProvider;
import freemarker.template.Configuration;
@@ -124,17 +125,30 @@
File ftlFile = new File(getBasePath(), ftlFilename);
freemarker.template.Template template = createTemplateImplementation(ftlFile);
+ // template for computed columns entities
+ String ftlComputedFilename = "org/openbravo/base/gen/entityComputedColumns.ftl";
+ File ftlComputedFile = new File(getBasePath(), ftlComputedFilename);
+ freemarker.template.Template templateComputed = createTemplateImplementation(ftlComputedFile);
+
// process template & write file for each entity
List<Entity> entities = ModelProvider.getInstance().getModel();
for (Entity entity : entities) {
// If the entity is associated with a datasource based table, do not generate a Java file
- if (!entity.isDataSourceBased()) {
- String classfileName = entity.getClassName().replaceAll("\\.", "/") + ".java";
+ if (entity.isDataSourceBased()) {
+ continue;
+ }
+
+ File outFile;
+ String classfileName;
+ Writer outWriter = null;
+
+ if (!entity.isVirtualEntity()) {
+ classfileName = entity.getClassName().replaceAll("\\.", "/") + ".java";
log.debug("Generating file: " + classfileName);
- File outFile = new File(srcGenPath, classfileName);
+ outFile = new File(srcGenPath, classfileName);
new File(outFile.getParent()).mkdirs();
- Writer outWriter;
+ outWriter = null;
try {
outWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),
"UTF-8"));
@@ -143,6 +157,49 @@
processTemplate(template, data, outWriter);
} catch (IOException e) {
log.error("Error generating file: " + classfileName, e);
+ } finally {
+ if (outWriter != null) {
+ try {
+ outWriter.close();
+ } catch (IOException ignore) {
+ }
+ }
+ }
+ }
+
+ if (entity.hasComputedColumns()) {
+ classfileName = entity.getPackageName().replaceAll("\\.", "/") + "/"
+ + entity.getSimpleClassName() + Entity.COMPUTED_COLUMNS_CLASS_APPENDIX + ".java";
+ log.debug("Generating file: " + classfileName);
+ outFile = new File(srcGenPath, classfileName);
+ new File(outFile.getParent()).mkdirs();
+ outWriter = null;
+ try {
+ outWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),
+ "UTF-8"));
+ Map<String, Object> data = new HashMap<String, Object>();
+ data.put("entity", entity);
+
+ List<Property> properties = entity.getComputedColumnProperties();
+
+ properties.add(entity.getProperty("client"));
+ properties.add(entity.getProperty("organization"));
+
+ data.put("properties", properties);
+ List<String> imports = entity.getJavaImports(properties);
+ imports.remove("import org.openbravo.base.structure.ActiveEnabled;");
+ imports.remove("import org.openbravo.base.structure.Traceable;");
+ data.put("javaImports", imports);
+ processTemplate(templateComputed, data, outWriter);
+ } catch (IOException e) {
+ log.error("Error generating file: " + classfileName, e);
+ } finally {
+ if (outWriter != null) {
+ try {
+ outWriter.close();
+ } catch (IOException ignore) {
+ }
+ }
}
}
}
diff -r e18b9062f750 src/org/openbravo/base/gen/entity.ftl
--- a/src/org/openbravo/base/gen/entity.ftl Wed Jun 12 12:00:11 2013 +0200
+++ b/src/org/openbravo/base/gen/entity.ftl Tue Jun 18 13:09:15 2013 +0200
@@ -26,7 +26,7 @@
* under the License.
* The Original Code is Openbravo ERP.
* The Initial Developer of the Original Code is Openbravo SLU
- * All portions are Copyright (C) 2008-2011 Openbravo SLU
+ * All portions are Copyright (C) 2008-2013 Openbravo SLU
* All Rights Reserved.
* Contributor(s): ______________________________________.
************************************************************************
@@ -46,12 +46,23 @@
public static final String TABLE_NAME = "${entity.tableName}";
public static final String ENTITY_NAME = "${entity.name}";
<#list entity.properties as p>
+ <#if !p.computedColumn>
public static final String PROPERTY_${p.name?upper_case} = "${p.name}";
+ </#if>
</#list>
+
+ <#if entity.hasComputedColumns()>
+ // Computed columns properties, these properties cannot be directly accessed, they need
+ // to be read through _commputedColumns proxy. They cannot be directly used in HQL, OBQuery
+ // nor OBCriteria.
+ <#list entity.computedColumnProperties as p>
+ public static final String COMPUTED_COLUMN_${p.name?upper_case} = "${p.name}";
+ </#list>
+ </#if>
public ${entity.simpleClassName}() {
<#list entity.properties as p>
- <#if p.hasDefaultValue()>
+ <#if p.hasDefaultValue() && !p.computedColumn>
setDefaultValue(PROPERTY_${p.name?upper_case}, ${p.formattedDefaultValue});
</#if>
</#list>
@@ -71,7 +82,11 @@
<#if p.partOfCompositeId>
return ((Id)getId()).«getter((Property)p)»();
<#else>
+ <#if !p.computedColumn>
return (${p.shorterTypeName}) get(PROPERTY_${p.name?upper_case});
+ <#else>
+ return (${p.shorterTypeName}) get(COMPUTED_COLUMN_${p.name?upper_case});
+ </#if>
</#if>
}
@@ -82,7 +97,11 @@
<#if p.partOfCompositeId>
((Id)getId()).set${p.getterSetterName?cap_first}(${p.javaName});
<#else>
+ <#if !p.computedColumn>
set(PROPERTY_${p.name?upper_case}, ${p.javaName});
+ <#else>
+ set(COMPUTED_COLUMN_${p.name?upper_case}, ${p.javaName});
+ </#if>
</#if>
}
@@ -92,7 +111,12 @@
<#if p.oneToMany>
@SuppressWarnings("unchecked")
public ${theList(entity)}<${p.shorterNameTargetEntity}> get${p.name?cap_first}() {
- return (${theList(entity)}<${p.shorterNameTargetEntity}>) get(PROPERTY_${p.name?upper_case});
+ <#if !p.computedColumn>
+ return (${theList(entity)}<${p.shorterNameTargetEntity}>) get(PROPERTY_${p.name?upper_case});
+ <#else>
+ //ss
+ return (${theList(entity)}<${p.shorterNameTargetEntity}>) get(COMPUTED_COLUMN_${p.name?upper_case});
+ </#if>
}
public void set${p.getterSetterName?cap_first}(${theList(entity)}<${p.shorterNameTargetEntity}> ${p.name}) {
@@ -170,4 +194,17 @@
}
}
</#if>
+
+ <#if entity.hasComputedColumns()>
+ @Override
+ public Object get(String propName) {
+ <#list entity.computedColumnProperties as p>
+ if (COMPUTED_COLUMN_${p.name?upper_case}.equals(propName)){
+ return get_computedColumns().${getter(p)}();
+ }
+ </#list>
+
+ return super.get(propName);
+ }
+ </#if>
}
diff -r e18b9062f750 src/org/openbravo/base/gen/entityComputedColumns.ftl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openbravo/base/gen/entityComputedColumns.ftl Tue Jun 18 13:09:15 2013 +0200
@@ -0,0 +1,74 @@
+<#function getter p>
+ <#if p.boolean>
+ <#return "is" + p.getterSetterName?cap_first>
+ <#else>
+ <#return "get" + p.getterSetterName?cap_first>
+ </#if>
+</#function>
+
+<#function theList entity>
+ <#if entity.simpleClassName == "List">
+ <#return "java.util.List">
+ <#else>
+ <#return "List">
+ </#if>
+</#function>
+/*
+ *************************************************************************
+ * The contents of this file are subject to the Openbravo Public License
+ * Version 1.1 (the "License"), being the Mozilla Public License
+ * Version 1.1 with a permitted attribution clause; you may not use this
+ * file except in compliance with the License. You may obtain a copy of
+ * the License at http://www.openbravo.com/legal/license.html
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ * The Original Code is Openbravo ERP.
+ * The Initial Developer of the Original Code is Openbravo SLU
+ * All portions are Copyright (C) 2013 Openbravo SLU
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ ************************************************************************
+*/
+package ${entity.packageName};
+<#list javaImports as i>
+${i}
+</#list>
+/**
+ * Virtual entity class to hold computed columns for entity ${entity.name}.
+ *
+ * NOTE: This class should not be instantiated directly. To instantiate this
+ * class the {@link org.openbravo.base.provider.OBProvider} should be used.
+ */
+public class ${entity.simpleClassName}_ComputedColumns extends BaseOBObject implements ClientEnabled, OrganizationEnabled {
+ private static final long serialVersionUID = 1L;
+ public static final String ENTITY_NAME = "${entity.simpleClassName}_ComputedColumns";
+
+ <#list properties as p>
+ public static final String PROPERTY_${p.name?upper_case} = "${p.name}";
+ </#list>
+
+ @Override
+ public String getEntityName() {
+ return ENTITY_NAME;
+ }
+
+ <#list properties as p>
+ <#if !p.oneToMany>
+ <#if p.name?matches("Id")>
+ @Override
+ </#if>
+ public ${p.shorterTypeName} ${getter(p)}() {
+ return (${p.shorterTypeName}) get(PROPERTY_${p.name?upper_case});
+ }
+
+ <#if p.name?matches("Id")>
+ @Override
+ </#if>
+ public void set${p.getterSetterName?cap_first}(${p.shorterTypeName} ${p.javaName}) {
+ set(PROPERTY_${p.name?upper_case}, ${p.javaName});
+ }
+ </#if>
+ </#list>
+}
diff -r e18b9062f750 src/org/openbravo/base/model/Entity.java
--- a/src/org/openbravo/base/model/Entity.java Wed Jun 12 12:00:11 2013 +0200
+++ b/src/org/openbravo/base/model/Entity.java Tue Jun 18 13:09:15 2013 +0200
@@ -62,6 +62,7 @@
private List<Property> identifierProperties;
private List<Property> parentProperties;
private List<Property> orderByProperties;
+ private List<Property> computedColumnProperties;
private String name = null;
private String tableName;
@@ -83,6 +84,7 @@
private boolean isDeletable;
private boolean isView;
private boolean isDataSourceBased;
+ private boolean isVirtualEntity = false;
private EntityValidator entityValidator;
private AccessLevelChecker accessLevelChecker;
@@ -93,6 +95,8 @@
private String treeType;
private static final String DATASOURCEBASEDTABLE = "Datasource";
+ public static final String COMPUTED_COLUMNS_PROXY_PROPERTY = "_computedColumns";
+ public static final String COMPUTED_COLUMNS_CLASS_APPENDIX = "_ComputedColumns";
public String getTreeType() {
return treeType;
@@ -127,6 +131,8 @@
identifierProperties = new ArrayList<Property>();
parentProperties = new ArrayList<Property>();
orderByProperties = new ArrayList<Property>();
+ computedColumnProperties = new ArrayList<Property>();
+
// + 5 to take into account some additional properties for onetomany
// and such
propertiesByName = new HashMap<String, Property>(table.getColumns().size() + 5);
@@ -156,6 +162,22 @@
if (p.isOrderByProperty()) {
orderByProperties.add(p);
}
+ if (p.getSqlLogic() != null) {
+ computedColumnProperties.add(p);
+ }
+ }
+
+ if (hasComputedColumns()) {
+ // entities with computed columns have an extra property to proxy access to computed columns
+ // so they are lazily calculated
+ Property p = new Property();
+ p.setIndexInEntity(properties.size());
+ p.setEntity(this);
+ p.setName(COMPUTED_COLUMNS_PROXY_PROPERTY);
+ p.setColumnName(COMPUTED_COLUMNS_PROXY_PROPERTY);
+ p.setProxy(true);
+ properties.add(p);
+ propertiesByName.put(COMPUTED_COLUMNS_PROXY_PROPERTY, p);
}
Collections.sort(identifierProperties, new Comparator<Property>() {
@@ -202,6 +224,67 @@
}
/**
+ * Checks if entity has any computed column
+ */
+ public boolean hasComputedColumns() {
+ return computedColumnProperties != null && computedColumnProperties.size() > 0;
+ }
+
+ public List<Property> getComputedColumnProperties() {
+ return computedColumnProperties;
+ }
+
+ public void initializeComputedColumns(Table t, Entity e) {
+ setTableName(t.getTableName() + "_CC");
+ setTableId(t.getId() + "CC");
+ setClassName(e.getPackageName() + "." + e.getSimpleClassName()
+ + COMPUTED_COLUMNS_CLASS_APPENDIX);
+ setName(e.getSimpleClassName() + COMPUTED_COLUMNS_CLASS_APPENDIX);
+ setDeletable(false);
+ setMutable(false);
+ setInActive(true);
+ setView(false);
+ setModule(t.getThePackage().getModule());
+ isVirtualEntity = true;
+ properties = new ArrayList<Property>();
+ idProperties = new ArrayList<Property>();
+ identifierProperties = new ArrayList<Property>();
+ propertiesByName = new HashMap<String, Property>();
+ propertiesByColumnName = new HashMap<String, Property>();
+
+ for (final Column c : t.getColumns()) {
+ if (!(c.isKey() || c.getSqlLogic() != null
+ || "AD_Client_ID".equalsIgnoreCase(c.getColumnName()) || "AD_Org_ID".equalsIgnoreCase(c
+ .getColumnName()))) {
+ continue;
+ }
+ final Property p = new Property();
+ p.setEntity(this);
+ p.initializeFromColumn(c, false);
+ properties.add(p);
+ p.setIndexInEntity(properties.size() - 1);
+
+ propertiesByName.put(p.getName(), p);
+ if (p.getColumnName() != null) {
+ propertiesByColumnName.put(p.getColumnName().toLowerCase(), p);
+ }
+ if (p.isId()) {
+ idProperties.add(p);
+ }
+ }
+
+ }
+
+ /**
+ * Virtual entities are used when the main entity has computed columns. These virtual entities are
+ * mapped to the same database table the main entity is mapped to, they contain all the computed
+ * column properties, making in this way possible to lazily compute them.
+ */
+ public boolean isVirtualEntity() {
+ return isVirtualEntity;
+ }
+
+ /**
* Add a property to the internal arrays of properties (common, identifier, etc.)
*
* @param property
@@ -680,6 +763,10 @@
}
List<String> getJavaImportsInternal() {
+ return getJavaImportsInternal(properties);
+ }
+
+ List<String> getJavaImportsInternal(List<Property> propertyList) {
List<String> imports = new ArrayList<String>();
Set<String> simpleImports = new HashSet<String>();
imports.add("org.openbravo.base.structure.BaseOBObject");
@@ -703,7 +790,7 @@
}
// collect types of properties
- for (Property p : properties) {
+ for (Property p : propertyList) {
String fullType, simpleType;
if (p.isOneToMany()) {
// add list-type here to take precedence over model class named List
@@ -768,7 +855,11 @@
* Used to generate java import statements during generate.entities
*/
public List<String> getJavaImports() {
- List<String> imports = getJavaImportsInternal();
+ return getJavaImports(properties);
+ }
+
+ public List<String> getJavaImports(List<Property> propertyList) {
+ List<String> imports = getJavaImportsInternal(propertyList);
List<String> result = new ArrayList<String>();
String lastImport = "";
for (String i : imports) {
diff -r e18b9062f750 src/org/openbravo/base/model/ModelProvider.java
--- a/src/org/openbravo/base/model/ModelProvider.java Wed Jun 12 12:00:11 2013 +0200
+++ b/src/org/openbravo/base/model/ModelProvider.java Tue Jun 18 13:09:15 2013 +0200
@@ -229,6 +229,7 @@
entitiesWithTreeType = new ArrayList<Entity>();
for (final Table t : tables) {
log.debug("Building model for table " + t.getName());
+
final Entity e = new Entity();
e.initialize(t);
model.add(e);
@@ -241,6 +242,19 @@
if (e.getTreeType() != null) {
entitiesWithTreeType.add(e);
}
+
+ if (e.hasComputedColumns()) {
+ // When the entity has computed columns, an extra virtual entity is generated in order to
+ // access these computed columns through a proxy that allows to compute them lazily.
+ log.debug("Generating computed columns proxy entity for entity " + e.getName());
+ final Entity computedColsEntity = new Entity();
+ computedColsEntity.initializeComputedColumns(t, e);
+
+ model.add(computedColsEntity);
+ entitiesByClassName.put(computedColsEntity.getClassName(), computedColsEntity);
+ entitiesByName.put(computedColsEntity.getName(), computedColsEntity);
+ entitiesByTableId.put(computedColsEntity.getTableId(), computedColsEntity);
+ }
}
// in the second pass set all the referenceProperties
@@ -276,8 +290,8 @@
e.getTableName(), p.getColumnName()));
if (mandatory != null) {
p.setMandatory(mandatory);
- } else if (p.getSqlLogic() == null) {
- // only log in case the sql logic is not set
+ } else if (!p.isComputedColumn() && !p.isProxy() && !e.isVirtualEntity()) {
+ // only log in case the sql logic is not set and it is not a proxy
log.warn("Column " + p + " mandatory setting not found in the database metadata. "
+ "A cause can be that the column does not exist in the database schema");
}
@@ -494,6 +508,21 @@
setReferencedPropertiesForTable(translatableColumns, t);
}
+ // setting referenced properties for client/org in proxy entities for computed columns
+ for (final Entity entity : model) {
+ if (!entity.isVirtualEntity()) {
+ continue;
+ }
+
+ Entity clientEntity = ModelProvider.getInstance().getEntity("ADClient");
+ entity.getPropertyByColumnName("AD_Client_ID").setReferencedProperty(
+ clientEntity.getPropertyByColumnName("AD_Client_ID"));
+
+ Entity orgEntity = ModelProvider.getInstance().getEntity("Organization");
+ entity.getPropertyByColumnName("AD_Org_ID").setReferencedProperty(
+ orgEntity.getPropertyByColumnName("AD_Org_ID"));
+ }
+
return translatableColumns;
}
diff -r e18b9062f750 src/org/openbravo/base/model/Property.java
--- a/src/org/openbravo/base/model/Property.java Wed Jun 12 12:00:11 2013 +0200
+++ b/src/org/openbravo/base/model/Property.java Tue Jun 18 13:09:15 2013 +0200
@@ -121,6 +121,7 @@
private Integer seqno;
private boolean usedSequence;
+ private boolean isProxy;
/**
* Initializes this Property using the information from the Column.
@@ -129,7 +130,13 @@
* the column used to initialize this Property.
*/
public void initializeFromColumn(Column fromColumn) {
- fromColumn.setProperty(this);
+ initializeFromColumn(fromColumn, true);
+ }
+
+ void initializeFromColumn(Column fromColumn, boolean setPropertyInColumn) {
+ if (setPropertyInColumn) {
+ fromColumn.setProperty(this);
+ }
setId(fromColumn.isKey());
setIdentifier(fromColumn.isIdentifier());
setParent(fromColumn.isParent());
@@ -191,7 +198,7 @@
setInactive(!fromColumn.isActive());
setModule(fromColumn.getModule());
-
+ isProxy = false;
}
// TODO: remove this hack when possible
@@ -603,6 +610,8 @@
} else {
typeName = getPrimitiveType().getName();
}
+ } else if ("_computedColumns".equals(getColumnName())) {
+ return getEntity().getSimpleClassName() + "_ComputedColumns";
} else if (getTargetEntity() == null) {
log.warn("ERROR NO REFERENCETYPE " + getEntity().getName() + "." + getColumnName());
return "java.lang.Object";
@@ -791,7 +800,7 @@
final boolean isSpecialEnumerateCase = value instanceof String
&& getColumnName().equalsIgnoreCase("changeprojectstatus");
if (!isSpecialEnumerateCase) {
- getDomainType().checkIsValidValue(this, value);
+ // getDomainType().checkIsValidValue(this, value);
}
// check property characteristics
@@ -883,6 +892,27 @@
this.isCompositeId = isCompositeId;
}
+ /**
+ * A property is a computed column when it has sql logic, in this case it is calculated based on a
+ * sql formula and is accessed through a proxy.
+ */
+ public boolean isComputedColumn() {
+ return getSqlLogic() != null;
+ }
+
+ /**
+ * Proxy properties are used to access to computed columns. Computed columns are not directly
+ * within the entity they are defined in, but in a extra entity that is accessed through a proxy,
+ * in this way computed columns are lazily calculated.
+ */
+ public boolean isProxy() {
+ return isProxy;
+ }
+
+ public void setProxy(boolean isProxy) {
+ this.isProxy = isProxy;
+ }
+
public List<Property> getIdParts() {
return idParts;
}
diff -r e18b9062f750 src/org/openbravo/dal/core/DalMappingGenerator.java
--- a/src/org/openbravo/dal/core/DalMappingGenerator.java Wed Jun 12 12:00:11 2013 +0200
+++ b/src/org/openbravo/dal/core/DalMappingGenerator.java Tue Jun 18 13:09:15 2013 +0200
@@ -21,9 +21,13 @@
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
import org.apache.log4j.Logger;
import org.hibernate.type.YesNoType;
@@ -48,6 +52,7 @@
private static final Logger log = Logger.getLogger(DalMappingGenerator.class);
private final static String HIBERNATE_FILE_PROPERTY = "hibernate.hbm.file";
+ private final static String HIBERNATE_READ_FILE_PROPERTY = "hibernate.hbm.readFile";
private final static String TEMPLATE_FILE = "template.hbm.xml";
private final static String MAIN_TEMPLATE_FILE = "template_main.hbm.xml";
@@ -78,11 +83,30 @@
* @return the generated Hibernate mapping (corresponds to what is found in a hbm.xml file)
*/
public String generateMapping() {
+ final String hibernateFileLocation = OBPropertiesProvider.getInstance()
+ .getOpenbravoProperties().getProperty(HIBERNATE_FILE_PROPERTY);
+ final String readMappingFromFile = OBPropertiesProvider.getInstance().getOpenbravoProperties()
+ .getProperty(HIBERNATE_READ_FILE_PROPERTY);
+
+ if (hibernateFileLocation != null && readMappingFromFile != null
+ && Boolean.parseBoolean(readMappingFromFile)) {
+ try {
+ File hbm = new File(hibernateFileLocation);
+ if (hbm.exists()) {
+ log.info("Reading mapping from " + hibernateFileLocation);
+ FileInputStream fis = new FileInputStream(hbm);
+ return readFile(fis);
+ }
+ } catch (Exception e) {
+ log.error("Error reading mapping file, generating it instead", e);
+ }
+ }
+
final ModelProvider mp = ModelProvider.getInstance();
final StringBuilder sb = new StringBuilder();
for (final Entity e : mp.getModel()) {
// Do not map datasource based tables
- if (!e.isDataSourceBased()) {
+ if (!e.isDataSourceBased() && !e.isVirtualEntity()) {
final String entityMapping = generateMapping(e);
sb.append(entityMapping);
}
@@ -94,9 +118,6 @@
log.debug(result);
}
- final String hibernateFileLocation = OBPropertiesProvider.getInstance()
- .getOpenbravoProperties().getProperty(HIBERNATE_FILE_PROPERTY);
-
if (hibernateFileLocation != null) {
try {
final File f = new File(hibernateFileLocation);
@@ -128,6 +149,7 @@
// create the content by first getting the id
final StringBuilder content = new StringBuilder();
+ content.append(TAB2);
if (entity.getMappingClass() == null) {
content.append("<tuplizer entity-mode=\"dynamic-map\" "
+ "class=\"org.openbravo.dal.core.OBDynamicTuplizer\"/>\n\n");
@@ -143,6 +165,7 @@
}
content.append(NL);
+ List<Property> computedColumns = new ArrayList<Property>();
// now handle the standard columns
for (final Property p : entity.getProperties()) {
if (p.isId()) { // && p.isPrimitive()) { // handled separately
@@ -156,7 +179,9 @@
if (p.isOneToMany()) {
content.append(generateOneToMany(p));
} else {
- if (p.isPrimitive()) {
+ if (p.getSqlLogic() != null) {
+ computedColumns.add(p);
+ } else if (p.isPrimitive()) {
content.append(generatePrimitiveMapping(p));
} else {
content.append(generateReferenceMapping(p));
@@ -164,14 +189,73 @@
}
}
+ if (!computedColumns.isEmpty()) {
+ // create a proxy property for all computed columns
+ content.append(generateComputedColumnsMapping(entity));
+ }
+
if (entity.isActiveEnabled()) {
- content.append(getActiveFilter());
+ content.append(TAB2 + getActiveFilter());
}
hbm = hbm.replace("content", content.toString());
+
+ if (!computedColumns.isEmpty()) {
+ hbm = hbm + generateComputedColumnsClassMapping(entity, computedColumns);
+ }
return hbm;
}
+ private String generateComputedColumnsClassMapping(Entity entity, List<Property> computedColumns) {
+ String hbm = getClassTemplateContents();
+ String entityName = getComputedColumnsEntityName(entity);
+ hbm = hbm.replaceAll("<class", "<class name=\"" + entity.getPackageName() + "." + entityName
+ + "\" ");
+ hbm = hbm.replaceAll("mappingName", entityName);
+ hbm = hbm.replaceAll("tableName", entity.getTableName());
+ hbm = hbm.replaceAll("ismutable", "false");
+
+ final StringBuilder content = new StringBuilder();
+ content.append(TAB2
+ + "<tuplizer entity-mode=\"pojo\" class=\"org.openbravo.dal.core.OBTuplizer\"/>" + NL + NL);
+ content.append(generateStandardID(entity) + NL);
+
+ content
+ .append(TAB2
+ + "<many-to-one name=\"client\" column=\"AD_Client_ID\" not-null=\"true\" update=\"false\" insert=\"false\" entity-name=\"ADClient\" access=\"org.openbravo.dal.core.OBDynamicPropertyHandler\"/>"
+ + NL);
+ content
+ .append(TAB2
+ + "<many-to-one name=\"organization\" column=\"AD_Org_ID\" not-null=\"true\" update=\"false\" insert=\"false\" entity-name=\"Organization\" access=\"org.openbravo.dal.core.OBDynamicPropertyHandler\"/>"
+ + NL + NL);
+
+ for (Property p : computedColumns) {
+ if (p.isPrimitive()) {
+ content.append(generatePrimitiveMapping(p));
+ } else {
+ content.append(generateReferenceMapping(p));
+ }
+ }
+ hbm = hbm.replace("content", content.toString());
+ return hbm;
+ }
+
+ private String generateComputedColumnsMapping(Entity entity) {
+ Check.isTrue(entity.getIdProperties().size() == 1,
+ "Computed columns are not supported in entities with composited ID");
+ StringBuffer sb = new StringBuffer();
+ final Property p = entity.getIdProperties().get(0);
+ sb.append(TAB2
+ + "<many-to-one name=\"_computedColumns\" update=\"false\" insert=\"false\" access=\"org.openbravo.dal.core.OBDynamicPropertyHandler\" ");
+ sb.append("column=\"" + p.getColumnName() + "\" ");
+ sb.append("entity-name=\"" + getComputedColumnsEntityName(entity) + "\"/>" + NL);
+ return sb.toString();
+ }
+
+ private String getComputedColumnsEntityName(Entity entity) {
+ return entity.getSimpleClassName() + "_ComputedColumns";
+ }
+
private String getActiveFilter() {
return "<filter name=\"activeFilter\" condition=\":activeParam = isActive\"/>\n";
}
@@ -219,8 +303,12 @@
private String generateReferenceMapping(Property p) {
if (p.getTargetEntity() == null) {
- return "<!-- Unsupported reference type " + p.getName() + " of entity "
- + p.getEntity().getName() + "-->" + NL;
+ if (p.isProxy()) {
+ return "";
+ } else {
+ return "<!-- Unsupported reference type " + p.getName() + " of entity "
+ + p.getEntity().getName() + "-->" + NL;
+ }
}
final StringBuffer sb = new StringBuffer();
if (p.isOneToOne()) {
@@ -235,6 +323,7 @@
} else {
sb.append("column=\"" + p.getColumnName() + "\"");
}
+
// cascade=\
// "save-update\"
if (p.isMandatory()) {
@@ -309,7 +398,7 @@
sb.append(TAB3 + "<one-to-many entity-name=\"" + p.getTargetEntity().getName() + "\"/>" + NL);
if (p.getTargetEntity().isActiveEnabled()) {
- sb.append(getActiveFilter());
+ sb.append(TAB3 + getActiveFilter());
}
sb.append(TAB2 + "</bag>" + NL);
@@ -378,8 +467,12 @@
}
private String readFile(String fileName) {
+ return readFile(getClass().getResourceAsStream(fileName));
+ }
+
+ private String readFile(InputStream is) {
try {
- final InputStreamReader fr = new InputStreamReader(getClass().getResourceAsStream(fileName));
+ final InputStreamReader fr = new InputStreamReader(is);
final BufferedReader br = new BufferedReader(fr);
try {
String line;
diff -r e18b9062f750 src/org/openbravo/dal/core/template.hbm.xml
--- a/src/org/openbravo/dal/core/template.hbm.xml Wed Jun 12 12:00:11 2013 +0200
+++ b/src/org/openbravo/dal/core/template.hbm.xml Tue Jun 18 13:09:15 2013 +0200
@@ -2,3 +2,4 @@
<cache usage="read-write"/>
content
</class>
+
diff -r e18b9062f750 src/org/openbravo/dal/core/template_main.hbm.xml
--- a/src/org/openbravo/dal/core/template_main.hbm.xml Wed Jun 12 12:00:11 2013 +0200
+++ b/src/org/openbravo/dal/core/template_main.hbm.xml Tue Jun 18 13:09:15 2013 +0200
@@ -13,7 +13,7 @@
* under the License.
* The Original Code is Openbravo ERP.
* The Initial Developer of the Original Code is Openbravo SLU
- * All portions are Copyright (C) 2008-2011 Openbravo SLU
+ * All portions are Copyright (C) 2008-2013 Openbravo SLU
* All Rights Reserved.
* Contributor(s): ______________________________________.
************************************************************************
computed-columns-mp21.1.diff [^] (33,167 bytes) 2013-06-18 13:22 [Show Content] [Hide Content]diff -r 6a7b86469daa modules/org.openbravo.service.json/src/org/openbravo/service/json/AdvancedQueryBuilder.java
--- a/modules/org.openbravo.service.json/src/org/openbravo/service/json/AdvancedQueryBuilder.java Wed May 15 08:18:19 2013 +0000
+++ b/modules/org.openbravo.service.json/src/org/openbravo/service/json/AdvancedQueryBuilder.java Tue Jun 18 13:20:28 2013 +0200
@@ -11,7 +11,7 @@
* under the License.
* The Original Code is Openbravo ERP.
* The Initial Developer of the Original Code is Openbravo SLU
- * All portions are Copyright (C) 2009-2012 Openbravo SLU
+ * All portions are Copyright (C) 2009-2013 Openbravo SLU
* All Rights Reserved.
* Contributor(s): ______________________________________.
************************************************************************
@@ -474,6 +474,12 @@
// or uses the display column to display that in the grid
Property useProperty = property;
String useFieldName = fieldName.replace(DalUtil.FIELDSEPARATOR, DalUtil.DOT);
+
+ if (useProperty.isComputedColumn()) {
+ // Computed columns are not directly accessed but through _computedColumns proxy
+ useFieldName = Entity.COMPUTED_COLUMNS_PROXY_PROPERTY + DalUtil.DOT + useFieldName;
+ }
+
if (properties.size() >= 2) {
final Property refProperty = properties.get(properties.size() - 2);
if (refProperty.getDomainType() instanceof TableDomainType) {
@@ -1174,6 +1180,13 @@
}
}
} else {
+ Entity searchEntity = getEntity();
+ Property property = searchEntity.getProperty(localOrderBy);
+ if (property != null && property.isComputedColumn()) {
+ // Computed columns are accessed through proxy
+ localOrderBy = Entity.COMPUTED_COLUMNS_PROXY_PROPERTY + DalUtil.DOT + localOrderBy;
+ }
+
paths.add(localOrderBy);
}
diff -r 6a7b86469daa modules/org.openbravo.service.json/src/org/openbravo/service/json/JsonToDataConverter.java
--- a/modules/org.openbravo.service.json/src/org/openbravo/service/json/JsonToDataConverter.java Wed May 15 08:18:19 2013 +0000
+++ b/modules/org.openbravo.service.json/src/org/openbravo/service/json/JsonToDataConverter.java Tue Jun 18 13:20:28 2013 +0200
@@ -11,7 +11,7 @@
* under the License.
* The Original Code is Openbravo ERP.
* The Initial Developer of the Original Code is Openbravo SLU
- * All portions are Copyright (C) 2009-2011 Openbravo SLU
+ * All portions are Copyright (C) 2009-2013 Openbravo SLU
* All Rights Reserved.
* Contributor(s): ______________________________________.
************************************************************************
@@ -766,6 +766,7 @@
// do not change not updatable properties
// Updatable is a UI concept
// doNotHandleThisProperty |= !obObject.isNewOBObject() && !property.isUpdatable();
+ doNotHandleThisProperty |= property.isProxy();
return doNotHandleThisProperty;
}
diff -r 6a7b86469daa src/org/openbravo/base/gen/GenerateEntitiesTask.java
--- a/src/org/openbravo/base/gen/GenerateEntitiesTask.java Wed May 15 08:18:19 2013 +0000
+++ b/src/org/openbravo/base/gen/GenerateEntitiesTask.java Tue Jun 18 13:20:28 2013 +0200
@@ -36,6 +36,7 @@
import org.openbravo.base.exception.OBException;
import org.openbravo.base.model.Entity;
import org.openbravo.base.model.ModelProvider;
+import org.openbravo.base.model.Property;
import org.openbravo.base.session.OBPropertiesProvider;
import freemarker.template.Configuration;
@@ -124,26 +125,77 @@
File ftlFile = new File(getBasePath(), ftlFilename);
freemarker.template.Template template = createTemplateImplementation(ftlFile);
+ // template for computed columns entities
+ String ftlComputedFilename = "org/openbravo/base/gen/entityComputedColumns.ftl";
+ File ftlComputedFile = new File(getBasePath(), ftlComputedFilename);
+ freemarker.template.Template templateComputed = createTemplateImplementation(ftlComputedFile);
+
// process template & write file for each entity
List<Entity> entities = ModelProvider.getInstance().getModel();
for (Entity entity : entities) {
- String classfileName = entity.getClassName().replaceAll("\\.", "/") + ".java";
- log.debug("Generating file: " + classfileName);
- File outFile = new File(srcGenPath, classfileName);
- new File(outFile.getParent()).mkdirs();
+ File outFile;
+ String classfileName;
+ Writer outWriter = null;
- Writer outWriter;
- try {
- outWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),
- "UTF-8"));
- Map<String, Object> data = new HashMap<String, Object>();
+ if (!entity.isVirtualEntity()) {
+ classfileName = entity.getClassName().replaceAll("\\.", "/") + ".java";
+ log.debug("Generating file: " + classfileName);
+ outFile = new File(srcGenPath, classfileName);
+ new File(outFile.getParent()).mkdirs();
- data.put("entity", entity);
- processTemplate(template, data, outWriter);
- } catch (IOException e) {
- log.error("Error generating file: " + classfileName, e);
+ outWriter = null;
+ try {
+ outWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),
+ "UTF-8"));
+ Map<String, Object> data = new HashMap<String, Object>();
+
+ data.put("entity", entity);
+ processTemplate(template, data, outWriter);
+ } catch (IOException e) {
+ log.error("Error generating file: " + classfileName, e);
+ } finally {
+ if (outWriter != null) {
+ try {
+ outWriter.close();
+ } catch (IOException ignore) {
+ }
+ }
+ }
}
+ if (entity.hasComputedColumns()) {
+ classfileName = entity.getPackageName().replaceAll("\\.", "/") + "/"
+ + entity.getSimpleClassName() + Entity.COMPUTED_COLUMNS_CLASS_APPENDIX + ".java";
+ log.debug("Generating file: " + classfileName);
+ outFile = new File(srcGenPath, classfileName);
+ new File(outFile.getParent()).mkdirs();
+ outWriter = null;
+ try {
+ outWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),
+ "UTF-8"));
+ Map<String, Object> data = new HashMap<String, Object>();
+ data.put("entity", entity);
+ List<Property> properties = entity.getComputedColumnProperties();
+ properties.add(entity.getProperty("client"));
+ properties.add(entity.getProperty("organization"));
+ data.put("properties", properties);
+ List<String> imports = entity.getJavaImports(properties);
+ imports.remove("import org.openbravo.base.structure.ActiveEnabled;");
+ imports.remove("import org.openbravo.base.structure.Traceable;");
+ data.put("javaImports", imports);
+ processTemplate(templateComputed, data, outWriter);
+ } catch (IOException e) {
+ log.error("Error generating file: " + classfileName, e);
+ } finally {
+ if (outWriter != null) {
+ try {
+ outWriter.close();
+ } catch (IOException ignore) {
+ }
+ }
+
+ }
+ }
}
log.info("Generated " + entities.size() + " entities");
}
diff -r 6a7b86469daa src/org/openbravo/base/gen/entity.ftl
--- a/src/org/openbravo/base/gen/entity.ftl Wed May 15 08:18:19 2013 +0000
+++ b/src/org/openbravo/base/gen/entity.ftl Tue Jun 18 13:20:28 2013 +0200
@@ -26,7 +26,7 @@
* under the License.
* The Original Code is Openbravo ERP.
* The Initial Developer of the Original Code is Openbravo SLU
- * All portions are Copyright (C) 2008-2011 Openbravo SLU
+ * All portions are Copyright (C) 2008-2013 Openbravo SLU
* All Rights Reserved.
* Contributor(s): ______________________________________.
************************************************************************
@@ -46,12 +46,23 @@
public static final String TABLE_NAME = "${entity.tableName}";
public static final String ENTITY_NAME = "${entity.name}";
<#list entity.properties as p>
+ <#if !p.computedColumn>
public static final String PROPERTY_${p.name?upper_case} = "${p.name}";
+ </#if>
</#list>
+
+ <#if entity.hasComputedColumns()>
+ // Computed columns properties, these properties cannot be directly accessed, they need
+ // to be read through _commputedColumns proxy. They cannot be directly used in HQL, OBQuery
+ // nor OBCriteria.
+ <#list entity.computedColumnProperties as p>
+ public static final String COMPUTED_COLUMN_${p.name?upper_case} = "${p.name}";
+ </#list>
+ </#if>
public ${entity.simpleClassName}() {
<#list entity.properties as p>
- <#if p.hasDefaultValue()>
+ <#if p.hasDefaultValue() && !p.computedColumn>
setDefaultValue(PROPERTY_${p.name?upper_case}, ${p.formattedDefaultValue});
</#if>
</#list>
@@ -71,7 +82,11 @@
<#if p.partOfCompositeId>
return ((Id)getId()).«getter((Property)p)»();
<#else>
+ <#if !p.computedColumn>
return (${p.shorterTypeName}) get(PROPERTY_${p.name?upper_case});
+ <#else>
+ return (${p.shorterTypeName}) get(COMPUTED_COLUMN_${p.name?upper_case});
+ </#if>
</#if>
}
@@ -82,7 +97,11 @@
<#if p.partOfCompositeId>
((Id)getId()).set${p.getterSetterName?cap_first}(${p.javaName});
<#else>
+ <#if !p.computedColumn>
set(PROPERTY_${p.name?upper_case}, ${p.javaName});
+ <#else>
+ set(COMPUTED_COLUMN_${p.name?upper_case}, ${p.javaName});
+ </#if>
</#if>
}
@@ -92,7 +111,12 @@
<#if p.oneToMany>
@SuppressWarnings("unchecked")
public ${theList(entity)}<${p.shorterNameTargetEntity}> get${p.name?cap_first}() {
- return (${theList(entity)}<${p.shorterNameTargetEntity}>) get(PROPERTY_${p.name?upper_case});
+ <#if !p.computedColumn>
+ return (${theList(entity)}<${p.shorterNameTargetEntity}>) get(PROPERTY_${p.name?upper_case});
+ <#else>
+ //ss
+ return (${theList(entity)}<${p.shorterNameTargetEntity}>) get(COMPUTED_COLUMN_${p.name?upper_case});
+ </#if>
}
public void set${p.getterSetterName?cap_first}(${theList(entity)}<${p.shorterNameTargetEntity}> ${p.name}) {
@@ -170,4 +194,17 @@
}
}
</#if>
+
+ <#if entity.hasComputedColumns()>
+ @Override
+ public Object get(String propName) {
+ <#list entity.computedColumnProperties as p>
+ if (COMPUTED_COLUMN_${p.name?upper_case}.equals(propName)){
+ return get_computedColumns().${getter(p)}();
+ }
+ </#list>
+
+ return super.get(propName);
+ }
+ </#if>
}
diff -r 6a7b86469daa src/org/openbravo/base/gen/entityComputedColumns.ftl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openbravo/base/gen/entityComputedColumns.ftl Tue Jun 18 13:20:28 2013 +0200
@@ -0,0 +1,74 @@
+<#function getter p>
+ <#if p.boolean>
+ <#return "is" + p.getterSetterName?cap_first>
+ <#else>
+ <#return "get" + p.getterSetterName?cap_first>
+ </#if>
+</#function>
+
+<#function theList entity>
+ <#if entity.simpleClassName == "List">
+ <#return "java.util.List">
+ <#else>
+ <#return "List">
+ </#if>
+</#function>
+/*
+ *************************************************************************
+ * The contents of this file are subject to the Openbravo Public License
+ * Version 1.1 (the "License"), being the Mozilla Public License
+ * Version 1.1 with a permitted attribution clause; you may not use this
+ * file except in compliance with the License. You may obtain a copy of
+ * the License at http://www.openbravo.com/legal/license.html
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ * The Original Code is Openbravo ERP.
+ * The Initial Developer of the Original Code is Openbravo SLU
+ * All portions are Copyright (C) 2013 Openbravo SLU
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ ************************************************************************
+*/
+package ${entity.packageName};
+<#list javaImports as i>
+${i}
+</#list>
+/**
+ * Virtual entity class to hold computed columns for entity ${entity.name}.
+ *
+ * NOTE: This class should not be instantiated directly. To instantiate this
+ * class the {@link org.openbravo.base.provider.OBProvider} should be used.
+ */
+public class ${entity.simpleClassName}_ComputedColumns extends BaseOBObject implements ClientEnabled, OrganizationEnabled {
+ private static final long serialVersionUID = 1L;
+ public static final String ENTITY_NAME = "${entity.simpleClassName}_ComputedColumns";
+
+ <#list properties as p>
+ public static final String PROPERTY_${p.name?upper_case} = "${p.name}";
+ </#list>
+
+ @Override
+ public String getEntityName() {
+ return ENTITY_NAME;
+ }
+
+ <#list properties as p>
+ <#if !p.oneToMany>
+ <#if p.name?matches("Id")>
+ @Override
+ </#if>
+ public ${p.shorterTypeName} ${getter(p)}() {
+ return (${p.shorterTypeName}) get(PROPERTY_${p.name?upper_case});
+ }
+
+ <#if p.name?matches("Id")>
+ @Override
+ </#if>
+ public void set${p.getterSetterName?cap_first}(${p.shorterTypeName} ${p.javaName}) {
+ set(PROPERTY_${p.name?upper_case}, ${p.javaName});
+ }
+ </#if>
+ </#list>
+}
diff -r 6a7b86469daa src/org/openbravo/base/model/Entity.java
--- a/src/org/openbravo/base/model/Entity.java Wed May 15 08:18:19 2013 +0000
+++ b/src/org/openbravo/base/model/Entity.java Tue Jun 18 13:20:28 2013 +0200
@@ -62,6 +62,7 @@
private List<Property> identifierProperties;
private List<Property> parentProperties;
private List<Property> orderByProperties;
+ private List<Property> computedColumnProperties;
private String name = null;
private String tableName;
@@ -82,6 +83,7 @@
private boolean isMutable;
private boolean isDeletable;
private boolean isView;
+ private boolean isVirtualEntity = false;
private EntityValidator entityValidator;
private AccessLevelChecker accessLevelChecker;
@@ -91,6 +93,9 @@
private String treeType;
+ public static final String COMPUTED_COLUMNS_PROXY_PROPERTY = "_computedColumns";
+ public static final String COMPUTED_COLUMNS_CLASS_APPENDIX = "_ComputedColumns";
+
public String getTreeType() {
return treeType;
}
@@ -123,6 +128,8 @@
identifierProperties = new ArrayList<Property>();
parentProperties = new ArrayList<Property>();
orderByProperties = new ArrayList<Property>();
+ computedColumnProperties = new ArrayList<Property>();
+
// + 5 to take into account some additional properties for onetomany
// and such
propertiesByName = new HashMap<String, Property>(table.getColumns().size() + 5);
@@ -152,6 +159,22 @@
if (p.isOrderByProperty()) {
orderByProperties.add(p);
}
+ if (p.getSqlLogic() != null) {
+ computedColumnProperties.add(p);
+ }
+ }
+
+ if (hasComputedColumns()) {
+ // entities with computed columns have an extra property to proxy access to computed columns
+ // so they are lazily calculated
+ Property p = new Property();
+ p.setIndexInEntity(properties.size());
+ p.setEntity(this);
+ p.setName(COMPUTED_COLUMNS_PROXY_PROPERTY);
+ p.setColumnName(COMPUTED_COLUMNS_PROXY_PROPERTY);
+ p.setProxy(true);
+ properties.add(p);
+ propertiesByName.put(COMPUTED_COLUMNS_PROXY_PROPERTY, p);
}
Collections.sort(identifierProperties, new Comparator<Property>() {
@@ -198,6 +221,67 @@
}
/**
+ * Checks if entity has any computed column
+ */
+ public boolean hasComputedColumns() {
+ return computedColumnProperties != null && computedColumnProperties.size() > 0;
+ }
+
+ public List<Property> getComputedColumnProperties() {
+ return computedColumnProperties;
+ }
+
+ public void initializeComputedColumns(Table t, Entity e) {
+ setTableName(t.getTableName() + "_CC");
+ setTableId(t.getId() + "CC");
+ setClassName(e.getPackageName() + "." + e.getSimpleClassName()
+ + COMPUTED_COLUMNS_CLASS_APPENDIX);
+ setName(e.getSimpleClassName() + COMPUTED_COLUMNS_CLASS_APPENDIX);
+ setDeletable(false);
+ setMutable(false);
+ setInActive(true);
+ setView(false);
+ setModule(t.getThePackage().getModule());
+ isVirtualEntity = true;
+ properties = new ArrayList<Property>();
+ idProperties = new ArrayList<Property>();
+ identifierProperties = new ArrayList<Property>();
+ propertiesByName = new HashMap<String, Property>();
+ propertiesByColumnName = new HashMap<String, Property>();
+
+ for (final Column c : t.getColumns()) {
+ if (!(c.isKey() || c.getSqlLogic() != null
+ || "AD_Client_ID".equalsIgnoreCase(c.getColumnName()) || "AD_Org_ID".equalsIgnoreCase(c
+ .getColumnName()))) {
+ continue;
+ }
+ final Property p = new Property();
+ p.setEntity(this);
+ p.initializeFromColumn(c, false);
+ properties.add(p);
+ p.setIndexInEntity(properties.size() - 1);
+
+ propertiesByName.put(p.getName(), p);
+ if (p.getColumnName() != null) {
+ propertiesByColumnName.put(p.getColumnName().toLowerCase(), p);
+ }
+ if (p.isId()) {
+ idProperties.add(p);
+ }
+ }
+
+ }
+
+ /**
+ * Virtual entities are used when the main entity has computed columns. These virtual entities are
+ * mapped to the same database table the main entity is mapped to, they contain all the computed
+ * column properties, making in this way possible to lazily compute them.
+ */
+ public boolean isVirtualEntity() {
+ return isVirtualEntity;
+ }
+
+ /**
* Add a property to the internal arrays of properties (common, identifier, etc.)
*
* @param property
@@ -668,6 +752,10 @@
}
List<String> getJavaImportsInternal() {
+ return getJavaImportsInternal(properties);
+ }
+
+ List<String> getJavaImportsInternal(List<Property> propertyList) {
List<String> imports = new ArrayList<String>();
Set<String> simpleImports = new HashSet<String>();
imports.add("org.openbravo.base.structure.BaseOBObject");
@@ -691,7 +779,7 @@
}
// collect types of properties
- for (Property p : properties) {
+ for (Property p : propertyList) {
String fullType, simpleType;
if (p.isOneToMany()) {
// add list-type here to take precedence over model class named List
@@ -756,7 +844,11 @@
* Used to generate java import statements during generate.entities
*/
public List<String> getJavaImports() {
- List<String> imports = getJavaImportsInternal();
+ return getJavaImports(properties);
+ }
+
+ public List<String> getJavaImports(List<Property> propertyList) {
+ List<String> imports = getJavaImportsInternal(propertyList);
List<String> result = new ArrayList<String>();
String lastImport = "";
for (String i : imports) {
diff -r 6a7b86469daa src/org/openbravo/base/model/ModelProvider.java
--- a/src/org/openbravo/base/model/ModelProvider.java Wed May 15 08:18:19 2013 +0000
+++ b/src/org/openbravo/base/model/ModelProvider.java Tue Jun 18 13:20:28 2013 +0200
@@ -226,6 +226,19 @@
if (e.getTreeType() != null) {
entitiesWithTreeType.add(e);
}
+
+ if (e.hasComputedColumns()) {
+ // When the entity has computed columns, an extra virtual entity is generated in order to
+ // access these computed columns through a proxy that allows to compute them lazily.
+ log.info("Generating computed columns proxy entity for entity " + e.getName());
+ final Entity computedColsEntity = new Entity();
+ computedColsEntity.initializeComputedColumns(t, e);
+
+ model.add(computedColsEntity);
+ entitiesByClassName.put(computedColsEntity.getClassName(), computedColsEntity);
+ entitiesByName.put(computedColsEntity.getName(), computedColsEntity);
+ entitiesByTableId.put(computedColsEntity.getTableId(), computedColsEntity);
+ }
}
// in the second pass set all the referenceProperties
@@ -261,8 +274,8 @@
e.getTableName(), p.getColumnName()));
if (mandatory != null) {
p.setMandatory(mandatory);
- } else if (p.getSqlLogic() == null) {
- // only log in case the sql logic is not set
+ } else if (!p.isComputedColumn() && !p.isProxy() && !e.isVirtualEntity()) {
+ // only log in case the sql logic is not set and it is not a proxy
log.warn("Column " + p + " mandatory setting not found in the database metadata. "
+ "A cause can be that the column does not exist in the database schema");
}
@@ -501,6 +514,21 @@
}
}
+ // setting referenced properties for client/org in proxy entities for computed columns
+ for (final Entity entity : model) {
+ if (!entity.isVirtualEntity()) {
+ continue;
+ }
+
+ Entity clientEntity = ModelProvider.getInstance().getEntity("ADClient");
+ entity.getPropertyByColumnName("AD_Client_ID").setReferencedProperty(
+ clientEntity.getPropertyByColumnName("AD_Client_ID"));
+
+ Entity orgEntity = ModelProvider.getInstance().getEntity("Organization");
+ entity.getPropertyByColumnName("AD_Org_ID").setReferencedProperty(
+ orgEntity.getPropertyByColumnName("AD_Org_ID"));
+ }
+
return translatableColumns;
}
diff -r 6a7b86469daa src/org/openbravo/base/model/Property.java
--- a/src/org/openbravo/base/model/Property.java Wed May 15 08:18:19 2013 +0000
+++ b/src/org/openbravo/base/model/Property.java Tue Jun 18 13:20:28 2013 +0200
@@ -120,6 +120,7 @@
private Integer seqno;
private boolean usedSequence;
+ private boolean isProxy;
/**
* Initializes this Property using the information from the Column.
@@ -128,7 +129,13 @@
* the column used to initialize this Property.
*/
public void initializeFromColumn(Column fromColumn) {
- fromColumn.setProperty(this);
+ initializeFromColumn(fromColumn, true);
+ }
+
+ void initializeFromColumn(Column fromColumn, boolean setPropertyInColumn) {
+ if (setPropertyInColumn) {
+ fromColumn.setProperty(this);
+ }
setId(fromColumn.isKey());
setIdentifier(fromColumn.isIdentifier());
setParent(fromColumn.isParent());
@@ -190,7 +197,7 @@
setInactive(!fromColumn.isActive());
setModule(fromColumn.getModule());
-
+ isProxy = false;
}
// TODO: remove this hack when possible
@@ -602,6 +609,8 @@
} else {
typeName = getPrimitiveType().getName();
}
+ } else if ("_computedColumns".equals(getColumnName())) {
+ return getEntity().getSimpleClassName() + "_ComputedColumns";
} else if (getTargetEntity() == null) {
log.warn("ERROR NO REFERENCETYPE " + getEntity().getName() + "." + getColumnName());
return "java.lang.Object";
@@ -790,7 +799,7 @@
final boolean isSpecialEnumerateCase = value instanceof String
&& getColumnName().equalsIgnoreCase("changeprojectstatus");
if (!isSpecialEnumerateCase) {
- getDomainType().checkIsValidValue(this, value);
+ // getDomainType().checkIsValidValue(this, value);
}
// check property characteristics
@@ -882,6 +891,27 @@
this.isCompositeId = isCompositeId;
}
+ /**
+ * A property is a computed column when it has sql logic, in this case it is calculated based on a
+ * sql formula and is accessed through a proxy.
+ */
+ public boolean isComputedColumn() {
+ return getSqlLogic() != null;
+ }
+
+ /**
+ * Proxy properties are used to access to computed columns. Computed columns are not directly
+ * within the entity they are defined in, but in a extra entity that is accessed through a proxy,
+ * in this way computed columns are lazily calculated.
+ */
+ public boolean isProxy() {
+ return isProxy;
+ }
+
+ public void setProxy(boolean isProxy) {
+ this.isProxy = isProxy;
+ }
+
public List<Property> getIdParts() {
return idParts;
}
diff -r 6a7b86469daa src/org/openbravo/dal/core/DalMappingGenerator.java
--- a/src/org/openbravo/dal/core/DalMappingGenerator.java Wed May 15 08:18:19 2013 +0000
+++ b/src/org/openbravo/dal/core/DalMappingGenerator.java Tue Jun 18 13:20:28 2013 +0200
@@ -23,7 +23,10 @@
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
import org.apache.log4j.Logger;
import org.hibernate.type.YesNoType;
@@ -48,6 +51,7 @@
private static final Logger log = Logger.getLogger(DalMappingGenerator.class);
private final static String HIBERNATE_FILE_PROPERTY = "hibernate.hbm.file";
+ private final static String HIBERNATE_READ_FILE_PROPERTY = "hibernate.hbm.readFile";
private final static String TEMPLATE_FILE = "template.hbm.xml";
private final static String MAIN_TEMPLATE_FILE = "template_main.hbm.xml";
@@ -78,11 +82,15 @@
* @return the generated Hibernate mapping (corresponds to what is found in a hbm.xml file)
*/
public String generateMapping() {
+ final String hibernateFileLocation = OBPropertiesProvider.getInstance()
+ .getOpenbravoProperties().getProperty(HIBERNATE_FILE_PROPERTY);
final ModelProvider mp = ModelProvider.getInstance();
final StringBuilder sb = new StringBuilder();
for (final Entity e : mp.getModel()) {
- final String entityMapping = generateMapping(e);
- sb.append(entityMapping);
+ if (!e.isVirtualEntity()) {
+ final String entityMapping = generateMapping(e);
+ sb.append(entityMapping);
+ }
}
final String mainTemplate = readFile(MAIN_TEMPLATE_FILE);
final String result = mainTemplate.replace("contentPlaceholder", sb.toString());
@@ -91,9 +99,6 @@
log.debug(result);
}
- final String hibernateFileLocation = OBPropertiesProvider.getInstance()
- .getOpenbravoProperties().getProperty(HIBERNATE_FILE_PROPERTY);
-
if (hibernateFileLocation != null) {
try {
final File f = new File(hibernateFileLocation);
@@ -125,6 +130,7 @@
// create the content by first getting the id
final StringBuilder content = new StringBuilder();
+ content.append(TAB2);
if (entity.getMappingClass() == null) {
content.append("<tuplizer entity-mode=\"dynamic-map\" "
+ "class=\"org.openbravo.dal.core.OBDynamicTuplizer\"/>\n\n");
@@ -140,6 +146,7 @@
}
content.append(NL);
+ List<Property> computedColumns = new ArrayList<Property>();
// now handle the standard columns
for (final Property p : entity.getProperties()) {
if (p.isId()) { // && p.isPrimitive()) { // handled separately
@@ -153,7 +160,9 @@
if (p.isOneToMany()) {
content.append(generateOneToMany(p));
} else {
- if (p.isPrimitive()) {
+ if (p.getSqlLogic() != null) {
+ computedColumns.add(p);
+ } else if (p.isPrimitive()) {
content.append(generatePrimitiveMapping(p));
} else {
content.append(generateReferenceMapping(p));
@@ -161,14 +170,73 @@
}
}
+ if (!computedColumns.isEmpty()) {
+ // create a proxy property for all computed columns
+ content.append(generateComputedColumnsMapping(entity));
+ }
+
if (entity.isActiveEnabled()) {
- content.append(getActiveFilter());
+ content.append(TAB2 + getActiveFilter());
}
hbm = hbm.replace("content", content.toString());
+
+ if (!computedColumns.isEmpty()) {
+ hbm = hbm + generateComputedColumnsClassMapping(entity, computedColumns);
+ }
return hbm;
}
+ private String generateComputedColumnsClassMapping(Entity entity, List<Property> computedColumns) {
+ String hbm = getClassTemplateContents();
+ String entityName = getComputedColumnsEntityName(entity);
+ hbm = hbm.replaceAll("<class", "<class name=\"" + entity.getPackageName() + "." + entityName
+ + "\" ");
+ hbm = hbm.replaceAll("mappingName", entityName);
+ hbm = hbm.replaceAll("tableName", entity.getTableName());
+ hbm = hbm.replaceAll("ismutable", "false");
+
+ final StringBuilder content = new StringBuilder();
+ content.append(TAB2
+ + "<tuplizer entity-mode=\"pojo\" class=\"org.openbravo.dal.core.OBTuplizer\"/>" + NL + NL);
+ content.append(generateStandardID(entity) + NL);
+
+ content
+ .append(TAB2
+ + "<many-to-one name=\"client\" column=\"AD_Client_ID\" not-null=\"true\" update=\"false\" insert=\"false\" entity-name=\"ADClient\" access=\"org.openbravo.dal.core.OBDynamicPropertyHandler\"/>"
+ + NL);
+ content
+ .append(TAB2
+ + "<many-to-one name=\"organization\" column=\"AD_Org_ID\" not-null=\"true\" update=\"false\" insert=\"false\" entity-name=\"Organization\" access=\"org.openbravo.dal.core.OBDynamicPropertyHandler\"/>"
+ + NL + NL);
+
+ for (Property p : computedColumns) {
+ if (p.isPrimitive()) {
+ content.append(generatePrimitiveMapping(p));
+ } else {
+ content.append(generateReferenceMapping(p));
+ }
+ }
+ hbm = hbm.replace("content", content.toString());
+ return hbm;
+ }
+
+ private String generateComputedColumnsMapping(Entity entity) {
+ Check.isTrue(entity.getIdProperties().size() == 1,
+ "Computed columns are not supported in entities with composited ID");
+ StringBuffer sb = new StringBuffer();
+ final Property p = entity.getIdProperties().get(0);
+ sb.append(TAB2
+ + "<many-to-one name=\"_computedColumns\" update=\"false\" insert=\"false\" access=\"org.openbravo.dal.core.OBDynamicPropertyHandler\" ");
+ sb.append("column=\"" + p.getColumnName() + "\" ");
+ sb.append("entity-name=\"" + getComputedColumnsEntityName(entity) + "\"/>" + NL);
+ return sb.toString();
+ }
+
+ private String getComputedColumnsEntityName(Entity entity) {
+ return entity.getSimpleClassName() + "_ComputedColumns";
+ }
+
private String getActiveFilter() {
return "<filter name=\"activeFilter\" condition=\":activeParam = isActive\"/>\n";
}
@@ -216,8 +284,12 @@
private String generateReferenceMapping(Property p) {
if (p.getTargetEntity() == null) {
- return "<!-- Unsupported reference type " + p.getName() + " of entity "
- + p.getEntity().getName() + "-->" + NL;
+ if (p.isProxy()) {
+ return "";
+ } else {
+ return "<!-- Unsupported reference type " + p.getName() + " of entity "
+ + p.getEntity().getName() + "-->" + NL;
+ }
}
final StringBuffer sb = new StringBuffer();
if (p.isOneToOne()) {
@@ -232,6 +304,7 @@
} else {
sb.append("column=\"" + p.getColumnName() + "\"");
}
+
// cascade=\
// "save-update\"
if (p.isMandatory()) {
@@ -306,7 +379,7 @@
sb.append(TAB3 + "<one-to-many entity-name=\"" + p.getTargetEntity().getName() + "\"/>" + NL);
if (p.getTargetEntity().isActiveEnabled()) {
- sb.append(getActiveFilter());
+ sb.append(TAB3 + getActiveFilter());
}
sb.append(TAB2 + "</bag>" + NL);
@@ -375,8 +448,12 @@
}
private String readFile(String fileName) {
+ return readFile(getClass().getResourceAsStream(fileName));
+ }
+
+ private String readFile(InputStream is) {
try {
- final InputStreamReader fr = new InputStreamReader(getClass().getResourceAsStream(fileName));
+ final InputStreamReader fr = new InputStreamReader(is);
final BufferedReader br = new BufferedReader(fr);
try {
String line;
diff -r 6a7b86469daa src/org/openbravo/dal/core/template.hbm.xml
--- a/src/org/openbravo/dal/core/template.hbm.xml Wed May 15 08:18:19 2013 +0000
+++ b/src/org/openbravo/dal/core/template.hbm.xml Tue Jun 18 13:20:28 2013 +0200
@@ -2,3 +2,4 @@
<cache usage="read-write"/>
content
</class>
+
diff -r 6a7b86469daa src/org/openbravo/dal/core/template_main.hbm.xml
--- a/src/org/openbravo/dal/core/template_main.hbm.xml Wed May 15 08:18:19 2013 +0000
+++ b/src/org/openbravo/dal/core/template_main.hbm.xml Tue Jun 18 13:20:28 2013 +0200
@@ -13,7 +13,7 @@
* under the License.
* The Original Code is Openbravo ERP.
* The Initial Developer of the Original Code is Openbravo SLU
- * All portions are Copyright (C) 2008-2011 Openbravo SLU
+ * All portions are Copyright (C) 2008-2013 Openbravo SLU
* All Rights Reserved.
* Contributor(s): ______________________________________.
************************************************************************
|