Skip to content

Conversation

@ThuF
Copy link
Contributor

@ThuF ThuF commented Dec 23, 2025

TODO:

  • Add support for "precision" and "scale"!

Fixes: #5574

let projectFile = {
"guid": parameters.projectName,
"actions": []
}

Check notice

Code scanning / CodeQL

Semicolon insertion Note

Avoid automated semicolon insertion (92% of all statements in
the enclosing function
have an explicit semicolon).

Copilot Autofix

AI 1 day ago

In general, to fix this kind of issue, you add an explicit semicolon at the end of statements that currently depend on JavaScript’s automatic semicolon insertion. This makes the statement boundaries explicit and avoids subtle parsing differences if the code is refactored or minified.

For this specific case, the let projectFile = { ... } declaration in components/template/template-application-dao-v2/src/main/resources/META-INF/dirigible/template-application-dao-v2/project.json.mjs should be terminated with a semicolon immediately after the closing } of the object literal. Concretely, change line 9 from } to };. No other logic, imports, or definitions are needed, and this will not alter runtime behavior.

Suggested changeset 1
components/template/template-application-dao-v2/src/main/resources/META-INF/dirigible/template-application-dao-v2/project.json.mjs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/components/template/template-application-dao-v2/src/main/resources/META-INF/dirigible/template-application-dao-v2/project.json.mjs b/components/template/template-application-dao-v2/src/main/resources/META-INF/dirigible/template-application-dao-v2/project.json.mjs
--- a/components/template/template-application-dao-v2/src/main/resources/META-INF/dirigible/template-application-dao-v2/project.json.mjs
+++ b/components/template/template-application-dao-v2/src/main/resources/META-INF/dirigible/template-application-dao-v2/project.json.mjs
@@ -6,7 +6,7 @@
 	let projectFile = {
 		"guid": parameters.projectName,
 		"actions": []
-	}
+	};
 	const newProjectFile = JSON.stringify(projectFile);
 
 	let currenctWorkspace = workspace.getWorkspace(parameters.workspaceName);
EOF
@@ -6,7 +6,7 @@
let projectFile = {
"guid": parameters.projectName,
"actions": []
}
};
const newProjectFile = JSON.stringify(projectFile);

let currenctWorkspace = workspace.getWorkspace(parameters.workspaceName);
Copilot is powered by AI and may make mistakes. Always verify output.
try (Session session = getSessionFactory().openSession()) {
Transaction transaction = session.beginTransaction();
Object id = session.save(type, object);
Object id = session.save(type, data);

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
Session.save
should be avoided because it has been deprecated.

Copilot Autofix

AI 1 day ago

In general, the way to fix this is to stop using the deprecated Session.save method and switch to a non-deprecated alternative that preserves the same behavior. For Hibernate, the common replacement for save is saveOrUpdate (which will insert when the entity is transient and update when it is detached), or persist if you know you are always inserting new instances. Since this method is used for generic “save” semantics and returns the identifier, the closest drop-in without changing higher-level behavior is session.saveOrUpdate, followed by retrieving the identifier from the session via session.getIdentifier.

Concretely, in DataStore.save(String type, Map<String, Object> object), on line 155 we should remove the call to session.save(type, data) and replace it with a session.saveOrUpdate(type, data) call. Because saveOrUpdate returns void (unlike save), we then need to obtain the identifier of the persisted entity by calling session.getIdentifier(data) after saveOrUpdate. We keep the transaction boundaries and method signature unchanged so that callers still receive the identifier object as before. No new imports are required, as we are still using the same Session and Transaction types.

Suggested changeset 1
components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java b/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java
--- a/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java
+++ b/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java
@@ -152,7 +152,8 @@
         Map<String, Object> data = JsonTypeConverter.normalizeForEntity(object, type);
         try (Session session = getSessionFactory().openSession()) {
             Transaction transaction = session.beginTransaction();
-            Object id = session.save(type, data);
+            session.saveOrUpdate(type, data);
+            Object id = session.getIdentifier(data);
             transaction.commit();
             return id;
         }
EOF
@@ -152,7 +152,8 @@
Map<String, Object> data = JsonTypeConverter.normalizeForEntity(object, type);
try (Session session = getSessionFactory().openSession()) {
Transaction transaction = session.beginTransaction();
Object id = session.save(type, data);
session.saveOrUpdate(type, data);
Object id = session.getIdentifier(data);
transaction.commit();
return id;
}
Copilot is powered by AI and may make mistakes. Always verify output.
try (Session session = getSessionFactory().openSession()) {
Transaction transaction = session.beginTransaction();
session.saveOrUpdate(type, object);
session.saveOrUpdate(type, data);

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
Session.saveOrUpdate
should be avoided because it has been deprecated.

Copilot Autofix

AI 1 day ago

In general, the way to fix this issue is to replace calls to the deprecated Session.saveOrUpdate(...) API with the non-deprecated alternative recommended by Hibernate, typically Session.merge(...) (or other state transition methods like persist/update where appropriate). This preserves semantics while avoiding reliance on deprecated methods that might be removed.

For this specific code, upsert(String type, Map object) takes normalized entity data as a Map<String, Object> and then invokes session.saveOrUpdate(type, data). In Hibernate 5.2+ and 6.x, Session.merge(String entityName, Object object) is the non-deprecated analogue which either saves or updates the entity and returns the managed instance. To keep behavior the same and avoid deprecated APIs, we should change line 294 to call session.merge(type, data) instead of session.saveOrUpdate(type, data). The return value of merge is not used here, so we can ignore it. No additional imports are required since merge is already part of org.hibernate.Session. All other logic in the method (starting a transaction, committing, etc.) can remain unchanged.

Concretely:

  • In components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java, locate the upsert(String type, Map object) method.
  • Replace session.saveOrUpdate(type, data); with session.merge(type, data);.
  • No other code or imports need to be changed.
Suggested changeset 1
components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java b/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java
--- a/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java
+++ b/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java
@@ -291,7 +291,7 @@
 
         try (Session session = getSessionFactory().openSession()) {
             Transaction transaction = session.beginTransaction();
-            session.saveOrUpdate(type, data);
+            session.merge(type, data);
             transaction.commit();
         }
     }
EOF
@@ -291,7 +291,7 @@

try (Session session = getSessionFactory().openSession()) {
Transaction transaction = session.beginTransaction();
session.saveOrUpdate(type, data);
session.merge(type, data);
transaction.commit();
}
}
Copilot is powered by AI and may make mistakes. Always verify output.
try (Session session = getSessionFactory().openSession()) {
Transaction transaction = session.beginTransaction();
session.update(type, object);
session.update(type, data);

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
Session.update
should be avoided because it has been deprecated.

Copilot Autofix

AI 1 day ago

In general, to fix this issue you should stop calling the deprecated Session.update(String entityName, Object) method and instead either work with actual entity instances and call the non-deprecated Session.update(Object) or use another non-deprecated persistence mechanism (e.g., JPA EntityManager or explicit SQL). In this class, the store is designed around dynamic Map<String, Object> data and an entity-name string, which makes direct replacement with Session.update(Object) impractical without a larger redesign. The most targeted fix is to replace the deprecated call with an explicit native SQL UPDATE constructed from the data map and executed via Session.createNativeQuery, while still using the existing ParametersSetter utility for binding parameters.

Concretely, in DataStore.update(String type, Map object), instead of session.update(type, data), we will:

  1. Extract the "id" (or primary key) from data and throw an exception if it is missing.
  2. Build a StringBuilder for a statement like UPDATE {type} SET col1 = :col1, col2 = :col2 ... WHERE id = :id.
  3. Use session.createNativeQuery(sql) to create a NativeQuery<?>.
  4. Use ParametersSetter to bind all non-id fields and the id field.
  5. Execute the update via executeUpdate() and commit the transaction.

This preserves the existing semantics of “update these fields for the given id” using the same normalized map, but no longer relies on the deprecated Hibernate method. It requires only modifying the body of the update(String type, Map object) method in DataStore.java and does not need new imports, since NativeQuery and ParametersSetter are already imported.

Suggested changeset 1
components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java b/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java
--- a/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java
+++ b/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/DataStore.java
@@ -316,9 +316,48 @@
     public void update(String type, Map object) {
         Map<String, Object> data = JsonTypeConverter.normalizeForEntity(object, type);
 
+        // Expecting primary key under key "id"
+        Object idValue = data.get("id");
+        if (idValue == null) {
+            throw new IllegalArgumentException("Missing primary key 'id' in data for update of type: " + type);
+        }
+
+        StringBuilder sqlBuilder = new StringBuilder();
+        sqlBuilder.append("UPDATE ").append(type).append(" SET ");
+
+        boolean first = true;
+        for (String key : data.keySet()) {
+            if ("id".equals(key)) {
+                continue;
+            }
+            if (!first) {
+                sqlBuilder.append(", ");
+            }
+            sqlBuilder.append(key).append(" = :").append(key);
+            first = false;
+        }
+        sqlBuilder.append(" WHERE id = :id");
+
+        String sql = sqlBuilder.toString();
+
         try (Session session = getSessionFactory().openSession()) {
             Transaction transaction = session.beginTransaction();
-            session.update(type, data);
+            NativeQuery<?> query = session.createNativeQuery(sql);
+
+            // Bind parameters for all fields except id
+            Map<String, Object> params = new HashMap<>();
+            for (Map.Entry<String, Object> entry : data.entrySet()) {
+                String key = entry.getKey();
+                if ("id".equals(key)) {
+                    continue;
+                }
+                params.put(key, entry.getValue());
+            }
+            // Bind id parameter
+            params.put("id", idValue);
+
+            ParametersSetter.setParameters(query, params);
+            query.executeUpdate();
             transaction.commit();
         }
     }
EOF
@@ -316,9 +316,48 @@
public void update(String type, Map object) {
Map<String, Object> data = JsonTypeConverter.normalizeForEntity(object, type);

// Expecting primary key under key "id"
Object idValue = data.get("id");
if (idValue == null) {
throw new IllegalArgumentException("Missing primary key 'id' in data for update of type: " + type);
}

StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("UPDATE ").append(type).append(" SET ");

boolean first = true;
for (String key : data.keySet()) {
if ("id".equals(key)) {
continue;
}
if (!first) {
sqlBuilder.append(", ");
}
sqlBuilder.append(key).append(" = :").append(key);
first = false;
}
sqlBuilder.append(" WHERE id = :id");

String sql = sqlBuilder.toString();

try (Session session = getSessionFactory().openSession()) {
Transaction transaction = session.beginTransaction();
session.update(type, data);
NativeQuery<?> query = session.createNativeQuery(sql);

// Bind parameters for all fields except id
Map<String, Object> params = new HashMap<>();
for (Map.Entry<String, Object> entry : data.entrySet()) {
String key = entry.getKey();
if ("id".equals(key)) {
continue;
}
params.put(key, entry.getValue());
}
// Bind id parameter
params.put("id", idValue);

ParametersSetter.setParameters(query, params);
query.executeUpdate();
transaction.commit();
}
}
Copilot is powered by AI and may make mistakes. Always verify output.

String precisionValue = extractValue.apply(argText, "precision");
if (precisionValue != null)
columnDetails.setPrecision(Integer.valueOf(precisionValue));

Check notice

Code scanning / CodeQL

Missing catch of NumberFormatException Note

Potential uncaught 'java.lang.NumberFormatException'.

Copilot Autofix

AI 1 day ago

In general, to fix this kind of problem, any conversion from a string to an integer (such as Integer.parseInt or Integer.valueOf) that operates on data derived from external or free-form input should be surrounded by a try/catch (NumberFormatException e) block, or otherwise validated before parsing. This allows the code to handle malformed values gracefully instead of throwing an uncaught runtime exception.

For this specific case in EntityParser.java, the best fix without changing existing functionality is to mirror the existing handling used for length. That is:

  • Wrap columnDetails.setPrecision(Integer.valueOf(precisionValue)); in a try/catch (NumberFormatException e) block.
  • On failure, log or print a warning (consistent with the length handling), and skip setting the precision.
  • Do the same for scaleValue and columnDetails.setScale(Integer.valueOf(scaleValue));, since it has the same risk and comes from the same source.

This can be implemented entirely within the shown method body; no new imports are strictly necessary if we keep using System.err.println as with the existing length parsing. Concretely, within components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/parser/EntityParser.java, around lines 453–459, replace the direct calls to Integer.valueOf(...) with try/catch blocks that log a warning and avoid throwing.

Suggested changeset 1
components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/parser/EntityParser.java

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/parser/EntityParser.java b/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/parser/EntityParser.java
--- a/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/parser/EntityParser.java
+++ b/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/parser/EntityParser.java
@@ -451,12 +451,22 @@
                         columnDetails.setDefaultValue(defaultValue);
 
                     String precisionValue = extractValue.apply(argText, "precision");
-                    if (precisionValue != null)
-                        columnDetails.setPrecision(Integer.valueOf(precisionValue));
+                    if (precisionValue != null) {
+                        try {
+                            columnDetails.setPrecision(Integer.valueOf(precisionValue));
+                        } catch (NumberFormatException e) {
+                            System.err.println("Warning: Could not parse precision value: " + precisionValue);
+                        }
+                    }
 
                     String scaleValue = extractValue.apply(argText, "scale");
-                    if (scaleValue != null)
-                        columnDetails.setScale(Integer.valueOf(scaleValue));
+                    if (scaleValue != null) {
+                        try {
+                            columnDetails.setScale(Integer.valueOf(scaleValue));
+                        } catch (NumberFormatException e) {
+                            System.err.println("Warning: Could not parse scale value: " + scaleValue);
+                        }
+                    }
                 }
             } else if ("OneToMany".equals(decoratorName)) {
                 // Expects @OneToMany(() => OrderItem, { table: '...', joinColumn: '...', ... })
EOF
@@ -451,12 +451,22 @@
columnDetails.setDefaultValue(defaultValue);

String precisionValue = extractValue.apply(argText, "precision");
if (precisionValue != null)
columnDetails.setPrecision(Integer.valueOf(precisionValue));
if (precisionValue != null) {
try {
columnDetails.setPrecision(Integer.valueOf(precisionValue));
} catch (NumberFormatException e) {
System.err.println("Warning: Could not parse precision value: " + precisionValue);
}
}

String scaleValue = extractValue.apply(argText, "scale");
if (scaleValue != null)
columnDetails.setScale(Integer.valueOf(scaleValue));
if (scaleValue != null) {
try {
columnDetails.setScale(Integer.valueOf(scaleValue));
} catch (NumberFormatException e) {
System.err.println("Warning: Could not parse scale value: " + scaleValue);
}
}
}
} else if ("OneToMany".equals(decoratorName)) {
// Expects @OneToMany(() => OrderItem, { table: '...', joinColumn: '...', ... })
Copilot is powered by AI and may make mistakes. Always verify output.

String scaleValue = extractValue.apply(argText, "scale");
if (scaleValue != null)
columnDetails.setScale(Integer.valueOf(scaleValue));

Check notice

Code scanning / CodeQL

Missing catch of NumberFormatException Note

Potential uncaught 'java.lang.NumberFormatException'.

Copilot Autofix

AI 1 day ago

To fix the problem, the calls that convert precisionValue and scaleValue from String to Integer should handle potential NumberFormatException in the same manner as the existing length conversion: wrap the conversion in a try/catch and handle invalid values gracefully (for example by logging a warning and skipping the assignment). This preserves current functionality for valid input while safely ignoring malformed numeric metadata instead of failing the entire parse.

Concretely, in EntityParser.java around lines 453–459, modify the blocks:

  • For precisionValue, change:

    if (precisionValue != null)
        columnDetails.setPrecision(Integer.valueOf(precisionValue));

    to:

    if (precisionValue != null && !precisionValue.isEmpty()) {
        try {
            columnDetails.setPrecision(Integer.parseInt(precisionValue));
        } catch (NumberFormatException e) {
            System.err.println("Warning: Could not parse precision value: " + precisionValue);
        }
    }
  • For scaleValue, change:

    if (scaleValue != null)
        columnDetails.setScale(Integer.valueOf(scaleValue));

    to:

    if (scaleValue != null && !scaleValue.isEmpty()) {
        try {
            columnDetails.setScale(Integer.parseInt(scaleValue));
        } catch (NumberFormatException e) {
            System.err.println("Warning: Could not parse scale value: " + scaleValue);
        }
    }

This mirrors the existing pattern used for length, introduces no new dependencies, and keeps behavior for valid numbers unchanged. No additional imports or methods are required.

Suggested changeset 1
components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/parser/EntityParser.java

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/parser/EntityParser.java b/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/parser/EntityParser.java
--- a/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/parser/EntityParser.java
+++ b/components/data/data-store/src/main/java/org/eclipse/dirigible/components/data/store/parser/EntityParser.java
@@ -451,12 +451,22 @@
                         columnDetails.setDefaultValue(defaultValue);
 
                     String precisionValue = extractValue.apply(argText, "precision");
-                    if (precisionValue != null)
-                        columnDetails.setPrecision(Integer.valueOf(precisionValue));
+                    if (precisionValue != null && !precisionValue.isEmpty()) {
+                        try {
+                            columnDetails.setPrecision(Integer.parseInt(precisionValue));
+                        } catch (NumberFormatException e) {
+                            System.err.println("Warning: Could not parse precision value: " + precisionValue);
+                        }
+                    }
 
                     String scaleValue = extractValue.apply(argText, "scale");
-                    if (scaleValue != null)
-                        columnDetails.setScale(Integer.valueOf(scaleValue));
+                    if (scaleValue != null && !scaleValue.isEmpty()) {
+                        try {
+                            columnDetails.setScale(Integer.parseInt(scaleValue));
+                        } catch (NumberFormatException e) {
+                            System.err.println("Warning: Could not parse scale value: " + scaleValue);
+                        }
+                    }
                 }
             } else if ("OneToMany".equals(decoratorName)) {
                 // Expects @OneToMany(() => OrderItem, { table: '...', joinColumn: '...', ... })
EOF
@@ -451,12 +451,22 @@
columnDetails.setDefaultValue(defaultValue);

String precisionValue = extractValue.apply(argText, "precision");
if (precisionValue != null)
columnDetails.setPrecision(Integer.valueOf(precisionValue));
if (precisionValue != null && !precisionValue.isEmpty()) {
try {
columnDetails.setPrecision(Integer.parseInt(precisionValue));
} catch (NumberFormatException e) {
System.err.println("Warning: Could not parse precision value: " + precisionValue);
}
}

String scaleValue = extractValue.apply(argText, "scale");
if (scaleValue != null)
columnDetails.setScale(Integer.valueOf(scaleValue));
if (scaleValue != null && !scaleValue.isEmpty()) {
try {
columnDetails.setScale(Integer.parseInt(scaleValue));
} catch (NumberFormatException e) {
System.err.println("Warning: Could not parse scale value: " + scaleValue);
}
}
}
} else if ("OneToMany".equals(decoratorName)) {
// Expects @OneToMany(() => OrderItem, { table: '...', joinColumn: '...', ... })
Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[EDM] Add New Templates with Decorators

2 participants