what is the xml mapping for json like this? - mybatis

Tables:
Create table development(
developmentName varchar(100) primary key,
description varchar(100)
);
Create table Student(
name varchar(100) primary key,
dateType varchar(100),
developmentName foreign key reference development(developmentName)
);
Input Json:
The json would come in the format
{ "DevelopmentName":"comp", "description":"Descrizione Tracciatro 1", "Student":[ {
"name":"String",
"dateType":"String",
"DevelopmentName":"comp"
},
{
"name":"String",
"date_type":"String"
"DevelopmentName":"comp"
}]}
DevelopmentName is primary key and which is foreign key for Student.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="it.eng.billing.mapper.Development">
<resultMap id="BaseResultMap" type="it.eng.billing.model.Development">
<result column="DevelopmentName" jdbcType="VARCHAR" property="DevelopmentName" />
<result column="description" jdbcType="VARCHAR" property="description" />
</resultMap>
<insert id="insert" parameterType="it.eng.billing.model.Development" >
insert into Development (DevelopmentName, description) values (#{DevelopmentName,jdbcType=VARCHAR},#{description,jdbcType=VARCHAR})
</insert>
<insert id="insertOptions" parameterType="it.eng.billing.model.Student">
INSERT INTO Student (name,DevelopmentName, datatype)
VALUES
<foreach collection="list" item="option" open="(" separator="),(" close=")" >
#{option.name},#{option.DevelopmentName},#{option.datatype}
</foreach>
</insert>
</mapper>
There is problem with the XML. The json is not getting saved into database.
public interface DevelopmentMapper{
default void save(DevelopmentDto record) {
insert(record);
insertOptions(record.getStudent());
}
from service class, calling developmentMapper.save(DevelopmentDto obj)

Related

MyBatis map-underscore-to-camel-case not working

I am using MyBatis 2.0.0 and have problem: I have a userId and status field that need to retrieve data so I'm using Mybatis for it.
but when I try to get the data the MyBatis don't work and I keep getting the same error.
I've tried to add the lines to application.properties
#mybatis entity scan packages
mybatis.type-aliases-package=com.cnoga.**.dao
#Mapper.xml location
mybatis.mapper-locations=classpath*:/mybatis/**/*.xml
mybatis.configuration.map-underscore-to-camel-case=true
but nothing happend and I still get my error.
I even tried to create the mybatis-config.xml
like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
</configuration>
and add this line to application.properties instead the lines above:
"mybatis.config-location=classpath:/mybatis-config.xml"
The mapper file:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.proj.user.dao.UserMapper" >
<select id="query4Regist" resultType="java.util.Map" parameterType="java.util.Map" >
select u.user_id userId,
status
from t_user u
left join t_user_group ug on u.user_id = ug.user_id
where u.is_del = 1
and u.status between 0 and 4
and (
u.email = #{account,jdbcType=NVARCHAR}
<if test="countryCode != null and countryCode != ''">
or (u.mobile = #{account,jdbcType=NVARCHAR} and u.country_code = #{countryCode,jdbcType=NVARCHAR})
</if>
)
and (u.region_name = #{regionName} or ug.group_id = 7 or ug.group_id = 8)
order by u.user_id desc offset 0 rows fetch next 1 rows only
</select>
</mapper>
I solved the problem!
The problem was using the map functions.
To use MYBATIS I built struct only for the values ​​I wanted to transfer and I just changed in all the places where I called the map functions to get data from the new struct

how can I create a table for another schema owner in hsql

I am writing test cases for a Spring batch application. I need to setup an HSQL database that mirrors a remote mainframe db2 database so I can run my tests quickly.
I have to setup the db table in such as way that the following SQL statement will succeed:
SELECT PROJ_TYP, SYS_CD, ... FROM CMNREF.CNTRCT_EXTRNL_KEY_REF_V WITH UR
I believe the CMNREF above is the Schema owner. How can I create that table and schema in HSQL db so this SELECT will work?
Update:
Doing some research on the topic here, I've learned that I need to do the following:
1) Use the SA default user to create new user CMNREF
2) Grant some ROLES to user CMNREF (i.e. SELECT)
However, I also noticed that the default user SA has the DBA role, which means it can do anything on that database.... so I wonder if I even need to bother with creating the 1 and 2 above....
My Test Case:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/load-BMS-data-job-launcher-context.xml" })
public class SimpleJobLaunchFunctionalTests {
#Autowired
private JobLauncherTestUtils jobLauncherUtils;
#Qualifier("jdbcTemplate")
#Autowired
private JdbcOperations jdbcTemplate;
#Qualifier("jdbcTemplateBMS")
#Autowired
private JdbcOperations jdbcTemplateBMS;
#Before
public void setUp() {
jdbcTemplateBMS.update("DELETE from CMNREF.CNTRCT_EXTRNL_KEY_REF_V");
jdbcTemplate.update("DELETE from SHADOW_BMS");
// jdbcTemplate.update("DELETE from CMNREF.CNTRCT_EXTRNL_KEY_REF_V");
Calendar calendar = Calendar.getInstance();
java.sql.Timestamp currentTimestamp = new java.sql.Timestamp(calendar.getTime().getTime());
// Insert one test data record
jdbcTemplate.update("INSERT INTO CMNREF.CNTRCT_EXTRNL_KEY_REF_V(PROJ_TYP_CD, SYS_CD, STAT_CK_CD, EXTRNL_KEY_CD, EXTRNL_SYS_CD, CNTRCT_NUM, PROJ_NUM, CNTRCT_LINE_ITM, CUST_NUM, CUST_CNTL_NUM, CUST_NM, CHRG_CD, PRDCT_ID, BILNG_CRNCY_CD, BILNG_ISO_CRNCY_CD, BILNG_CRNCY_DCM_NUM, CFTS_CRNCY_EXCH_RT, CFTS_CRNCY_EXCH_RT_EXPIR_DT, CHRG_CRNCY_CD, CHRG_ISO_CRNCY_CD, CTRY_CD, CMPNY_CD, OFFERING_CD, CNTL_GRP_CD, ORIG_CTRY_CD, ORIG_CMPNY_CD, ORIG_LOC_CD, NOTES_ID, INET_ID, CFTS_GBI_IND, CFTS_CHRG_TYP_CD, CFTS_SPCL_HNDL_CD, CC_PMT_METH_IND, STAT_CD, REFRESH_TMS) VALUES('PROJT1', 'SCD', 'STC', 'EXTKEY','EXTSYSCODE', 'CTR923','PROJN293', 23, 'CNUM32', 'CN', 'NAME THIS CUST', 'CHCD', '2903-920','BCD', 'BIC', 23, 1.345, '2017-01-23','CCC', 'CIC', 'CCD', 'IBM', '9203L-98', 'CTLGRP', 'USA', 'IBM001', 'OLC', 'me#us.ibm.com', 'ME/US/IBM/COM', 'G', 'T', 'H', 'P', 'OPEN', '2016-01-02 19:29:23.271' )");
}
My DB Script is there:
CREATE USER CMNREF PASSWORD 'pw';
CREATE SCHEMA CMNREF AUTHORIZATION DBA;
ALTER USER CMNREF SET INITIAL SCHEMA CMNREF;
SET SCHEMA CMNREF;
CREATE TABLE CMNREF.CNTRCT_EXTRNL_KEY_REF_V (
PROJ_TYP_CD VARCHAR(10),
SYS_CD VARCHAR(3),
STAT_CK_CD VARCHAR(12),
EXTRNL_KEY_CD VARCHAR(36),
EXTRNL_SYS_CD VARCHAR(10),
CNTRCT_NUM VARCHAR(15),
PROJ_NUM VARCHAR(8),
CNTRCT_LINE_ITM INTEGER,
CUST_NUM VARCHAR(8),
CUST_CNTL_NUM VARCHAR(2),
CUST_NM VARCHAR(35),
CHRG_CD VARCHAR(4),
PRDCT_ID VARCHAR(15),
BILNG_CRNCY_CD VARCHAR(3),
BILNG_ISO_CRNCY_CD VARCHAR(3),
BILNG_CRNCY_DCM_NUM SMALLINT,
CFTS_CRNCY_EXCH_RT DECIMAL,
CFTS_CRNCY_EXCH_RT_EXPIR_DT DATE,
CHRG_CRNCY_CD VARCHAR(3),
CHRG_ISO_CRNCY_CD VARCHAR(3),
CTRY_CD VARCHAR(3),
CMPNY_CD VARCHAR(10),
OFFERING_CD VARCHAR(8),
CNTL_GRP_CD VARCHAR(8),
ORIG_CTRY_CD VARCHAR(3),
ORIG_CMPNY_CD VARCHAR(10),
ORIG_LOC_CD VARCHAR(3),
NOTES_ID VARCHAR(100),
INET_ID VARCHAR(100),
CFTS_GBI_IND VARCHAR(1),
CFTS_CHRG_TYP_CD VARCHAR(1),
CFTS_SPCL_HNDL_CD VARCHAR(1),
CC_PMT_METH_IND VARCHAR(1),
STAT_CD VARCHAR(12),
REFRESH_TMS TIMESTAMP
) ;
My DataSources are defined here:
<!-- Initialise the database before every test case: -->
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="${batch.drop.script}"/>
<jdbc:script location="${batch.schema.script}"/>
<jdbc:script location="${batch.business.schema.script}"/>
</jdbc:initialize-database>
<!-- Initialize the mock BMS database -->
<jdbc:initialize-database data-source="BMSdataSource" ignore-failures="DROPS">
<jdbc:script location="${bmsmock.business.schema.script}"/>
</jdbc:initialize-database>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${batch.jdbc.driver}" />
<property name="url" value="${batch.jdbc.url}" />
<property name="username" value="${batch.jdbc.user}" />
<property name="password" value="${batch.jdbc.password}" />
<property name="maxActive" value="${batch.jdbc.pool.size}"/>
<property name="validationQuery" value="${batch.jdbc.validationQuery}"/>
<property name="testWhileIdle" value="${batch.jdbc.testWhileIdle}"/>
</bean>
<bean id="BMSdataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${bmsmock.jdbc.driver}" />
<property name="url" value="${bmsmock.jdbc.url}" />
<property name="username" value="${bmsmock.jdbc.user}" />
<property name="password" value="${bmsmock.jdbc.password}" />
<property name="maxActive" value="${bmsmock.jdbc.pool.size}"/>
<property name="validationQuery" value="${bmsmock.jdbc.validationQuery}"/>
<property name="testWhileIdle" value="${bmsmock.jdbc.testWhileIdle}"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" lazy-init="true">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Set up or detect a System property called "ENVIRONMENT" used to construct a properties file on the classpath. The default is "hsql". -->
<bean id="environment"
class="org.springframework.batch.support.SystemPropertyInitializer">
<property name="defaultValue" value="hsql"/>
<property name="keyName" value="ENVIRONMENT"/>
</bean>
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
depends-on="environment">
<property name="location" value="classpath:batch-${ENVIRONMENT}.properties" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="order" value="1" />
</bean>
My HSQLDB Properties are below:
batch.jdbc.driver=org.hsqldb.jdbcDriver
batch.jdbc.url=jdbc:hsqldb:mem:testdb;sql.enforce_strict_size=true;hsqldb.tx=mvcc
batch.jdbc.user=sa
batch.jdbc.password=
batch.jdbc.testWhileIdle=false
batch.jdbc.validationQuery=
batch.drop.script=classpath:/org/springframework/batch/core/schema-drop-hsqldb.sql
batch.schema.script=classpath:/org/springframework/batch/core/schema-hsqldb.sql
batch.business.schema.script=classpath:/db/custom-db-assets.sql
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer
batch.database.incrementer.parent=columnIncrementerParent
batch.lob.handler.class=org.springframework.jdbc.support.lob.DefaultLobHandler
batch.jdbc.pool.size=6
batch.grid.size=6
batch.verify.cursor.position=true
batch.isolationlevel=ISOLATION_SERIALIZABLE
batch.data.source.init=true
batch.table.prefix=BATCH_
bmsmock.jdbc.driver=org.hsqldb.jdbcDriver
bmsmock.jdbc.url=jdbc:hsqldb:mem:testbms;sql.enforce_strict_size=true;hsqldb.tx=mvcc
bmsmock.jdbc.user=SA
bmsmock.jdbc.password=
bmsmock.jdbc.pool.size=6
bmsmock.jdbc.testWhileIdle=false
bmsmock.jdbc.validationQuery=
bmsmock.business.schema.script=db/mock-bms-tables.sql
It is a good idea to use a different user without the DBA role.
In HSQLDB a user and a schema are separate concepts, with no automatic schema per user. What you have done is fine. Alternatively you can
CREATE USER CMNREF PASSWORD 'pw'
CREATE SCHEMA CMNREF AUTHORIZATION CMNREF
With the above, the CMNREF user owns a schema of the same name. It can execute all statements in its own schema.
update: In your settings the SA user is used for all the operations. You need to run the user creation section of the DB script you quoted with the user SA, then run the tests with the user CMNREF.
My script (attached) shows exactly how to do it. It's right, nothing wrong with the script or schema definition. My problem was that my test case was refering to the wrong Jdbc template when inserting data into the CMNREF.C* table.... the correct stmt should have been:
jdbcTemplateBMS**.update("INSERT INTO CMNREF.CNTRCT_E
Also, I need to turn on the DB2 mode for my sql script to work properly

Kie Workbench Execute Rules

May be this question has been asked number of times but I could not figure out the actual solution by going through them. I have a decision table in KIEWorkbench which takes the input from one fact and sets it into another fact. I am trying to call the rules by invoking the endpoint: http://localhost:8085/kie-server-6.4.0.Final-ee7/services/rest/server/containers/instances/pocResult
In the header, I have set the Content-Type as application/xml.
<batch-execution lookup="ksession">
<insert out-identifier="Subject">
<demo.pocFindResult.Subject>
<bCode> ABC</bCode>
<bGCode>XY</bGCode>
<pCode>L0001</pcode>
<subjectType>CA</subjectType>
</demo.pocFindResult.Subject>
</insert>
<fire-all-rules />
<get-objects out-identifier="Result">
<demo.pocFindResult.result/>
</get-objects>
My Decision table is as below:
package demo.pocFindResult;
//from row number: 1
rule "Row 1 findrules"
ruleflow-group "fire-rules"
dialect "java"
lock-on-active true
no-loop true
when
sub : Subject( bCode == "ABC" , bGCode == "XY" , subjectType == "CA" , pCode == "L0001" )
then
Result rs = new Result();
rs.setResultStartDate( "*TODAY" );
rs.setResultEndDate( "*YEAREND" );
rs.setResultContentStartDate( "*TODAY" );
rs.setResultContentEndDate( "*YEAREND" );
insert( rs );
end
How can I get the Result object as the response? Here is my response:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response type="SUCCESS" msg="Container pocResult successfully called.">
<execution-results>
<results>
<item key="Subject">
<value xsi:type="jaxbListWrapper" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<type>LIST</type>
</value>
</item>
</results>
<facts>
<item key="Subject"/>
</facts>
</execution-results>
</response>
I want the result object with the dates set.
What is the response you get?
I would firstly try to place fire-all-rules tag after your return object. Otherwise, I would try:
<batch-execution lookup="ksession">
<insert out-identifier="Subject">
<demo.pocFindResult.Subject>
<bCode> ABC</bCode>
<bGCode>XY</bGCode>
<pCode>L0001</pcode>
<subjectType>CA</subjectType>
</demo.pocFindResult.Subject>
</insert>
<insert out-identifier="Result" return-object="true" entry-point="DEFAULT">
<demo.pocFindResult.result/>
</insert>
<fire-all-rules/>
</batch-execution>
I am using JBPM 7.0.0-SNAPSHOT and got the same result as yours. When I used the same rules in 6.2.0.Final, I had result back.
EDIT:
The key problem is the header:
Authorization:Basic YWRtaW46YWRtaW4=
Content-Type:application/xml ,
Then I added another header:
"X-KIE-ContentType : XSTREAM"
<batch-execution lookup="defaultKieSession">
<insert return-object="true">
<com.bp.PageContext>
<ID>AID</ID>
</com.bp.PageContext>
</insert>
<insert out-identifier="Group" return-object="true">
<com.bp.GroupData>
</com.bp.GroupData>
</insert>
<insert out-identifier="ERR" return-object="true">
<com.bp.ErrorMessage/>
</insert>
<fire-all-rules/>
<get-objects/>
</batch-execution>
I've got the result back:
<org.kie.server.api.model.ServiceResponse>
<type>SUCCESS</type>
<msg>Container bpcontainr successfully called.</msg>
<result class="execution-results">
<result identifier="Group">
<com.bp,GroupData>
<Code>TEST,QA</Code>
</com.bp.GroupData>
</result>
<result identifier="ERR">
<com.bp.ErrorMessage/>
</result>
<fact-handle identifier="Group" external-form="0:8:567620710:567620710:8:DEFAULT:NON_TRAIT:com.bp.GroupData"/>
<fact-handle identifier="ERR" external-form="0:9:1581854082:1581854082:9:DEFAULT:NON_TRAIT:com.bp.ErrorMessage"/>
</result>
</org.kie.server.api.model.ServiceResponse>

Inserting new tag in XML in xmlDB

Is it possible to insert new tag in the XML which is there in the XML-database?
For example; below is my example which is exists in the database:
<PurchaseOrder
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation=
"http://localhost:8080/source/schemas/poSource/xsd/purchaseOrder.xsd">
<Reference>SBELL-2002100912333601PDT</Reference>
<Actions>
<Action>
<User>SVOLLMAN</User>
</Action>
</Actions>
<Reject/>
<Requestor>Sarah J. Bell</Requestor>
<User>SBELL</User>
<CostCenter>S30</CostCenter>
<SpecialInstructions>Air Mail</SpecialInstructions>
<LineItems>
<LineItem ItemNumber="1">
<Description>A Night to Remember</Description>
<Part Id="715515009058" UnitPrice="39.95" Quantity="2"/>
</LineItem>
</LineItems>
</PurchaseOrder>
I need to insert new tag <sender>xxxx</sender> where <costCente> is 'S30'.
After inserting/updating the XML, it should be like below:
<PurchaseOrder
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation=
"http://localhost:8080/source/schemas/poSource/xsd/purchaseOrder.xsd">
<Reference>SBELL-2002100912333601PDT</Reference>
<sender>xxx</sender>
<Actions>
<Action>
<User>SVOLLMAN</User>
</Action>
</Actions>
<Reject/>
<Requestor>Sarah J. Bell</Requestor>
<User>SBELL</User>
<CostCenter>S30</CostCenter>
<SpecialInstructions>Air Mail</SpecialInstructions>
<LineItems>
<LineItem ItemNumber="1">
<Description>A Night to Remember</Description>
<Part Id="715515009058" UnitPrice="39.95" Quantity="2"/>
</LineItem>
</LineItems>
</PurchaseOrder>
Is it really a possible scenario?
Can any one give me query to do this, if it is possible.
Thanks in advance.
Below query worked for me.
UPDATE TABLENAME dd SET dd.object_value =
insertXMLbefore(dd.object_value,
'/c:PurchaseOrder/c:Reference',
XMLType('<d:sender xmlns:d="http://www.w3.org/2001/XMLSchema-instance">XXXX</d:sender>'),
xmlns:c="http://www.w3.org/2001/XMLSchema-instance"')
WHERE dd.id in (SELECT dd.id FROM TABLENAME dd,XMLTable(XMLNAMESPACES(
DEFAULT 'http://www.w3.org/2001/XMLSchema-instance' AS "c"),
'/PurchaseOrder' PASSING dd.object_value COLUMNS sender VARCHAR2(8 CHAR) PATH 'c:PurchaseOrder/c:Reference/text()',
rp VARCHAR2(8 CHAR) PATH 'CostCenter/text()') li
WHERE sender is null and rp = 'S30');

How can I declare a Clustered key on a many-to-many relationship table in CodeFluent Entities with Sql Server Producer

I have a CodeFluent Entities Model such as:
<cf:project defaultNamespace="S5T" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" xmlns:cfx="http://www.softfluent.com/codefluent/modeler/2008/1" xmlns:cfmy="http://www.softfluent.com/codefluent/producers.mysql/2012/1" xmlns:cfom="http://www.softfluent.com/codefluent/producers.model/2005/1" xmlns:cfasp="http://www.softfluent.com/codefluent/producers.aspnet/2011/1" xmlns:cfaz="http://www.softfluent.com/codefluent/producers.sqlazure/2011/1" xmlns:cfps="http://www.softfluent.com/codefluent/producers.sqlserver/2005/1" defaultKeyPropertyTypeName="long" maxParameterNameLength="62" defaultConcurrencyMode="None" persistencePropertyNameFormat="{1}" defaultMethodAllowDynamicSort="false" defaultProducerProductionFlags="Default, Overwrite, RemoveDates" defaultMethodDistinct="false" createDefaultMethodForms="true" createDefaultApplication="false" createDefaultHints="false" productionFlags="Default, Overwrite, RemoveDates">
<cf:import path="Default.Surface.cfp" />
<cf:producer name="SQL Server" typeName="CodeFluent.Producers.SqlServer.SqlServerProducer, CodeFluent.Producers.SqlServer">
<cf:configuration produceViews="true" targetDirectory="..\Model7Bom\Persistence" connectionString="Server=MY-MACHINE\SQLEXPRESS;Database=model7;Integrated Security=true;Application Name=S5T;Password=MyPassword;User ID=MyUser" cfx:targetProject="..\Model7Bom\Model7Bom.vbproj" cfx:targetProjectLayout="Update, DontRemove" />
</cf:producer>
<cf:entity name="User" namespace="S5T">
<cf:property name="Id" key="true" cfps:hint="CLUSTERED" />
<cf:property name="Name" />
<cf:property name="Roles" typeName="{0}.RoleCollection" relationPropertyName="Users" />
</cf:entity>
<cf:entity name="Role" namespace="S5T">
<cf:property name="Id" key="true" cfps:hint="CLUSTERED" />
<cf:property name="Name" />
<cf:property name="Users" typeName="{0}.UserCollection" relationPropertyName="Roles" />
</cf:entity>
</cf:project>
I could sucessfully decorate the cf:property name="Id" on both entities with cfps:hint="CLUSTERED". This got me Sql Server producer to correctly output
CONSTRAINT [PK_Use_Id_Use] PRIMARY KEY CLUSTERED
CONSTRAINT [PK_Rol_Id_Rol] PRIMARY KEY CLUSTERED
as opposed to default NONCLUSTERED.
How can I accomplish that with the THIRD TABLE generated by the model, to accomodate the many to many relationship?
By default, the table creation generated snippet is such as:
CREATE TABLE [dbo].[Role_Users_User_Roles] (
[Id] [bigint] NOT NULL,
[Id2] [bigint] NOT NULL,
CONSTRAINT [PK_Roe_Id_Id2_Roe] PRIMARY KEY NONCLUSTERED
(
[Id],
[Id2]
) ON [PRIMARY]
)
However, if I decorate both properties with cfps:hint="CLUSTERED" as in:
cf:property name="Roles" typeName="{0}.RoleCollection" relationPropertyName="Users" cfps:hint="CLUSTERED" /
cf:property name="Users" typeName="{0}.UserCollection" relationPropertyName="Roles" cfps:hint="CLUSTERED" /
I get a snippet generated with PRIMARY KEY CLUSTERED for the PK in TABLE [dbo].[Role_Users_User_Roles], BUT, in addition, I get an UNDESIRED effect of having an incorrect script generated for adding relations (generated filename ...relations_add.sql), such as:
ALTER TABLE [dbo].[Role_Users_User_Roles] WITH NOCHECK ADD CONSTRAINT [FK_Roe_Id_Id_Rol] FOREIGN KEY (
[Id]
) REFERENCES [dbo].[Role](
[Id]
) CLUSTERED
Along with the error from Sql Server:
Error 3 SQL80001: Incorrect syntax near 'CLUSTERED'.
And CodeFluent Producer Error:
CodeFluentRuntimeDatabaseException: CF4116: Execution of file ...path..._relations_add.sql statement at line 2
I need all three PKs CLUSTERED in the three tables generated, but not the side effect of syntax error for generating the relations.
This is not supported out-of-the-box. The hint declared on the a relation is rarely used, more meant as a Foreign Key hint than a column hint. There are several options you can use to do this.
The easiest is to use a post-generation .SQL script to add the clustered setting manually. This is described here: How to: Execute custom T-SQL scripts with the Microsoft SQL Server producer.
You could also use the Patch Producer to remove the CLUSTERED word from the file once it has been created : Patch Producer
Otherwise, here is another solution that involves an aspect I've written as a sample here. You can save the following piece of XML as a file, and reference it as an aspect in your model.
This aspect will add the CLUSTERED hint to primary keys of all Many To Many tables inferred from entities that have CLUSTERED keys. It will add the hint before the table scripts are created and ran, and will remove it after (so it won't end up in the relations_add script).
<cf:project xmlns:cf="http://www.softfluent.com/codefluent/2005/1">
<!-- assembly references -->
<?code #reference name="CodeFluent.Producers.SqlServer.dll" ?>
<?code #reference name="CodeFluent.Runtime.Database.dll" ?>
<!-- namespace includes -->
<?code #namespace name="System" ?>
<?code #namespace name="System.Collections.Generic" ?>
<?code #namespace name="CodeFluent.Model.Code" ?>
<?code #namespace name="CodeFluent.Model.Persistence" ?>
<?code #namespace name="CodeFluent.Model.Code" ?>
<!-- add global code to listen to inference steps -->
<?code
Project.StepChanging += (sender1, e1) =>
{
if (e1.Step == ImportStep.End) // hook before production begins (end of inference pipeline)
{
var modifiedTables = ProjectHandler.AddClusteredHint(Project);
// get sql server producer and hook on production events
var sqlProducer = Project.Producers.GetProducerInstance<CodeFluent.Producers.SqlServer.SqlServerProducer>();
sqlProducer.Production += (sender, e) =>
{
// determine what SQL file has been created
// we want to remove hints once the table_diffs has been created, before relations_add is created
string script = e.GetDictionaryValue("filetype", null);
if (script == "TablesDiffsScript")
{
ProjectHandler.RemoveClusteredHint(modifiedTables);
}
};
}
};
?>
<!-- add member code to handle inference modification -->
<?code #member
public class ProjectHandler
{
public static IList<Table> AddClusteredHint(Project project)
{
var list = new List<Table>();
foreach (var table in project.Database.Tables)
{
// we're only interested by tables inferred from M:M relations
if (table.Relation == null || table.Relation.RelationType != RelationType.ManyToMany)
continue;
// check this table definition is ok for us
if (table.RelationKeyColumns.Count < 1 || table.RelationRelatedKeyColumns.Count < 1)
continue;
// check clustered is declared on both sides
string keyHint = GetSqlServerProducerHint(table.RelationKeyColumns[0].Property);
string relatedKeyHint = GetSqlServerProducerHint(table.RelationKeyColumns[0].Property);
if (keyHint.IndexOf("clustered", StringComparison.OrdinalIgnoreCase) < 0 ||
relatedKeyHint.IndexOf("clustered", StringComparison.OrdinalIgnoreCase) < 0)
continue;
// force hint now, we only need to do this on one of the keys, not all
table.PrimaryKey.Elements[0].SetAttribute("hint", CodeFluent.Producers.SqlServer.Constants.SqlServerProducerNamespaceUri, "clustered");
// remember this table
list.Add(table);
}
return list;
}
public static void RemoveClusteredHint(IEnumerable<Table> list)
{
foreach (var table in list)
{
table.PrimaryKey.Elements[0].RemoveAttribute("hint", CodeFluent.Producers.SqlServer.Constants.SqlServerProducerNamespaceUri);
}
}
// helper method to read XML element's hint attribute in the SQL Server Producer namespace
private static string GetSqlServerProducerHint(Node node)
{
if (node == null)
return null;
return node.GetAttributeValue<string>("hint", CodeFluent.Producers.SqlServer.Constants.SqlServerProducerNamespaceUri, null);
}
}
?>
</cf:project>