^ Initial Steps
project-folder structure and location of XML manifest
| ....
|- sdm SDM folder (any path, any name)
|--- go.dal root-file: an empty file named as 'php.dal', 'java.dal', 'cpp.dal', 'python.dal', or 'go.dal' (ensure lower case)
|--- sdm.xml declarations of DTO/model and/or DAO classes
|--- settings.xml common settings
|--- sdm.xsd ------ XSD-files for XML code completion and parsing
|--- settings.xsd ------
|--- dao.xsd ------
|--- ProjectsDao.xml external XML files as an option to declare DAO classes
| ...
| - .sdm config file of SDM sidebar tool-button (JetBrains only, none in Eclipse)
- Create/Open an SDM folder
- Create/Open (double-click) a root-file to enter the Plug-in GUI, switch to "Admin"
- Create XSD files, "sdm.xml", and "settings.xml"
- Click "settings.xml" to start editing. Read and follow the notes in XML comments. Save the changes.
- One of the buttons marked as [x] may be a good starting point. See also the Demo Projects
The contents of the ".sdm" file:
# more than one "*.dal" file in the same project is ok sdm/go.dal # path-name of the root-file from example above sdm/sdm.xml # add more files from SDM folder for quick access sdm/settings.xml src/my_favorite.php # the others are ok as well
^ settings.xml -> examples of JDBC settings
| JDBC Driver | XML tag in 'settings.xml' |
| MySQL | <jdbc jar="lib/mysql-connector-java-8.0.13.jar" class="com.mysql.cj.jdbc.Driver" url="jdbc:mysql://localhost/sakila" user="root" pwd="root"/> |
| Oracle | <jdbc jar="lib/ojdbc8.jar" class="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@localhost:1521:orcl" user="ORDERS" pwd="sa"/> |
| PostgreSQL | <jdbc jar="postgresql-42.2.9.jar" class="org.postgresql.Driver" url="jdbc:postgresql://localhost:5432/orders" user="postgres" pwd="sa"/> |
| SQL Server | <jdbc jar="mssql-jdbc-8.2.2.jre11.jar" class="com.microsoft.sqlserver.jdbc.SQLServerDriver" url="jdbc:sqlserver://localhost\SQLEXPRESS;databaseName=AdventureWorks2014" user="sa" pwd="root"/> |
| xerial / sqlite-jdbc | <jdbc jar="sqlite-jdbc-3.40.0.0.jar" class="org.sqlite.JDBC" url="jdbc:sqlite:$PROJECT_DIR$/northwindEF.sqlite" user="" pwd=""/> |
| H2 | <jdbc jar="h2-1.4.190.jar" class="org.h2.Driver" url="jdbc:h2:$PROJECT_DIR$/h2_orders" user="" pwd="" /> |
^ settings.xml -> type-map
Here you specify how the types detected by the code generator are rendered in the target code.
<type-map default="">
<type detected="java.lang.Integer" target="int64">
<type detected="java.lang.Double" target="float64">
<type detected="java.lang.Float" target="float64">
<type detected="java.lang.String" target="string">
<type detected="byte[]" target="[]byte"/>
<type detected="java.lang.Object" target="interface{}"/>
</type-map>The rules are:
- if the detected type is listed under 'detected', the matching 'target' is rendered
- if it is not, 'default' is rendered
- if 'default' is empty, the detected type is rendered as-is.
If you want to create a "type-map" from scratch, read this: misc.html#type-map.
If you need stronger typing in the generated DAO classes, read this: misc.html#strong-typing.
There may be some nuances depending on database/driver.
Example 1. The MySQL JDBC driver detects all field types correctly. At the same time, MySQL drivers for Go fetch all values as []uint8, so you may need to extend the type-map accordingly.
Example 2. The Oracle JDBC driver detects all numerics as "java.math.BigDecimal". At the same time, Go + "github.com/godror/godror" fetches all numerics as strings, so you may need to extend the type-map accordingly.
Example 3. For Java + Android + SQLite, you may need this:
<type-map default="">
<!-- On Dev. PC, it is Integer, but in Android run-time it is Long -->
<type detected="java.lang.Integer" target="java.lang.Long"/>
</type-map>
Example 4. The JDBC driver for SQLite3 detects date/time as "java.lang.String" or "java.lang.Object",
while Go + "github.com/mattn/go-sqlite3" fetches date/time as "time:time.Time".
In such cases, 'type-map' may not work, and you need explicit declarations:
<dto-class name="Orders" ref="get_orders.sql">
<field type="time:time.Time" column="o_date"/>
</dto-class>^ settings.xml -> macros
To generate complex fields like the ones in the example below
class Task(Base):
__tablename__ = 'tasks'
t_id = Column('t_id', Integer, primary_key=True, autoincrement=True)
g_id = Column('g_id', Integer, ForeignKey('groups.g_id'))
t_priority = Column('t_priority', Integer)
t_date = Column('t_date', String(65535))
t_subject = Column('t_subject', String(65535))
t_comments = Column('t_comments', String(65535))
you will need to extend the <macros> section of "settings.xml" and reference those macros from "settings.xml" => <type-map><type ... target="...:
<type-map default="">
<type detected="sa-java.lang.Integer" target="${sa-type}|0:Integer"/>
<type detected="sa-java.lang.Float" target="${sa-type}|0:Float"/>
<type detected="sa-java.lang.String" target="${sa-type}|0:String"/>
<type detected="sa-byte[]" target="${sa-type}|0:LargeBinary"/>
<type detected="sa-java.lang.Object" target="${sa-type-unknown}"/>
</type-map>
<macros>
<macro name="${sa-type-unknown}" value="${sa-column-base}|type:"/>
<macro name="${sa-type}" value="${sa-column-base}|type:, ${0}"/>
<macro name="${sa-column-base}">
<vm>
<![CDATA[= Column('${column}'${type}#if($fi.ColumnSize>0)($fi.ColumnSize)#end#if($fi.FK), ForeignKey('$fi.FK')#end
#if($fi.PK), primary_key=True#end
#if($fi.AI), autoincrement=True#end
#if(!$fi.PK)
#if($fi.Indexed), index=True#end
#if($fi.Unique), unique=True#end
#if($fi.Nullable), nullable=True#end
#end)${comment}]]>
</vm>
</macro>
</macros>Examples of the field-related macros are available in the plug-in GUI => tab "Admin" => [X] and in the Demo Projects: https://github.com/panedrone/sqldalmaker
To customize class-based macros, use this:
-
refs to entries of section <macros>:
<dto scope="" inheritance="" field-names-mode="0" macro="${dto}"/> <dao scope="" field-assign-mode="0" macro="${dao}"/> -
refs to external files:
<dto scope="" inheritance="" field-names-mode="0" macro="sdm/python.models.vm"/> <dao scope="" field-assign-mode="0" macro="sdm/python.dao.vm"/>
^ Separation of Concerns
The generated code never touches your database API directly — no PDO, no JDBC, no QtSql. Every such call lives in a hand-written Proxy class, and the generated code reaches it through a single interface: DataStore.
That one boundary is what keeps the generated code small, and what lets the same XML manifest produce working code in five languages. The generator emits calls to DataStore and nothing else — what sits behind it is yours to decide.
The example below shows how to organize this in Go:
- DataStore, taken as-is from the plug-in, tab 'Admin': https://github.com/panedrone/sdm_todolist_go_react_vue_npm_sqlite3/blob/master/internal/sqlx/dbal/sqlx_store.go
- Proxy and the choice of the driver, hand-written: https://github.com/panedrone/sdm_todolist_go_react_vue_npm_sqlite3/blob/master/internal/sqlx/dbal/sqlx_store_ex.go
- Usage in Generated Code: https://github.com/panedrone/sdm_todolist_go_react_vue_npm_sqlite3/blob/master/internal/sqlx/dbal/projects_dao.go
For more examples, see the Demo Projects and the plug-in GUI, tab 'Admin'.
| Underlying database API | Databases | |
|---|---|---|
| PHP | MySQL, Oracle, PostgreSQL, SQL Server, SQLite3 | |
| Java |
Available through JDBC: MySQL, Oracle, PostgreSQL, SQL Server, SQLite3, H2, HSQLDB, Derby, IBM DB2, IBM Informix, Firebird, etc. |
|
| C++ | Databases supported by Qt: http://doc.qt.io/qt-5/sql-driver.html | |
| Python | - | |
| Go | Tested with MySQL, Oracle, PostgreSQL, SQL Server, and SQLite3. |