Category Hierarchy Using Nested Set - propel

I am trying to create a category hierarchy using nested sets in Propel. I don't particularly care about the left/right stuff, I just want to be able to define a hierarchy.
I am having problems with propel complaining that a root node already exists with the given scope even though I am looking for an existing root node? I'm new to Propel and the nested set model so I'm really not sure what I'm doing wrong.
My table schema:
<table name="category">
<column name="id" type="integer" primaryKey="true" autoIncrement="true"/>
<column name="name" type="varchar" size="250" required="true" primaryString="true"/>
<behavior name="nested_set">
<parameter name="use_scope" value="true"/>
<parameter name="scope_column" value="scope"/>
<parameter name="left_column" value="left" />
<parameter name="right_column" value="right"/>
<parameter name="level_column" value="level"/>
</behavior>
<foreign-key foreignTable="category" onDelete="CASCADE" onUpdate="CASCADE">
<reference local="scope" foreign="id"/>
</foreign-key>
</table>
The function for creating the categories:
/**
* Takes an array of category names. The first name is taken as
* the root category and then each name is a child of the preceding
* name.
*/
function createCategories(array $names) {
$categories = [];
$root = CategoryQuery::create()
->filterByName($names[0])
->filterByLevel(0)
->findOneOrCreate();
if($root->isNew()) {
$root->save();
$root->setScopeValue($root->getId());
}
$categories[0] = $root;
foreach($names as $level => $name) {
if($level == 0) {
continue;
}
$category = CategoryQuery::create()
->filterByName($name)
->filterByScope($root->getScopeValue())
->findOneOrCreate();
if($category->isNew()) {
$category->insertAsFirstChildOf($categories[$level - 1]);
}
$categories[$level] = $category;
}
return $categories;
}

The issue was with the definition of a root node. I assumed that a root node was defined as one where the level is 0. Looking at the generated code it in fact defines a root as one where it's left value is 1.
To correctly have the node defined as a root you can call:
$root->makeRoot()
This error then goes away.

Related

Is it possible to convert CLOB data to String in Mybatis sql mapper xml?

I want to receive CLOB data of CONTENT column as java.lang.String value with only sql mapper definition.
Select query is something like this.
<select id="selectById">
<![CDATA[
SELECT
ID -- // NUMBER
, TITLE -- // VARCHAR2(2000)
, CONTENT -- // CLOB
FROM FAQ
WHERE ID = #{id}
]]>
</select>
Is it possible? I've googled over an hour and realized <resultMap /> can be the solution.
But, I can't figure out any more. Please help me.
I'll show you two approaches.
The first solution is to use <resultMap />.
You just need to specify javaType for the CONTENT column.
<resultMap type="map" id="faqRM">
<id property="ID" column="ID" />
<result property="TITLE" column="TITLE" />
<result property="CONTENT" column="CONTENT" javaType="string" />
</resultMap>
and specify resultMap in the <select />.
<select id="selectById" resultMap="faqRM">
The second approach is to specify the type handler for CLOB vs Object conversion globally in the config.
<typeHandlers>
<typeHandler javaType="java.lang.Object" jdbcType="CLOB"
handler="org.apache.ibatis.type.StringTypeHandler" />
</typeHandlers>
Then you can simply specify resultType="map" instead of defining <resultMap />.
<select id="selectById" resultType="map">
I should also mention that java.lang.String has smaller capacity than CLOB (2GB - 1 vs 4GB - 1 last time I checked).

Obfuscar tool ForceStringHiding

After hours finally got why my app crashes after obfuscating by Obfuscar. That is StringHiding in MyClass. So settings for module now is
<SkipStringHiding type="Myspacename.MyClass" name="*" />
Now I need to hide only some of the strings inside MyClass. For example
private const string TrialLicenseKey = "AEAF3-N4C7K-BWDTV-3CLZB-XXXXX";
I was trying some combinatons of settings, but strings are still visible in Reflector.
Is ForceStringHiding supported? What is for name parameter? String content, var name, etc.?
<ForceStringHiding type="Myspacename.MyClass" name="???" />
Can't get why i see unobfuscated private static strings in Reflector
static Debugging()
{
A = new object();
__public = "AOMRDQELD+0rFgbQxySAHrBpU3N8RF1i3rXkgSC79aXEgE=";
D = "ActivationHardwareId";
d = "LicenseKey";
E = "ActivationKey";
...
}
settings for Obfuscar
<Var name="KeepPublicApi" value="true" />
<Var name="HidePrivateApi" value="true" />
<Var namr="HideStrings" value="true" />

How to make a for loop work for xquery, in the context of 'contains'?

If we have a books.xml file like
<books>
<book isbn="123">
<collections>
<collection c="1" />
<collection c="2" />
<collection c="3" />
<collection c="4" />
</collections>
</book>
<book isbn="234">
<collections>
<collection c="1" />
<collection c="2" />
<collection c="3" />
<collection c="5" />
</collections>
</book>
</books>
I want to return all the ISBNs where both books belong to the same collections.
declare variable $doc := doc("books.xml");
<samecollections>
{
for $b1 in $doc/books/book
for $b2 in $doc/books/book
where $b1 << $b2 and
every $p in $b1/collections/collection
satisfies $p in $b2/collections/collection
return
...
}
</samecollections>
How would I make something like that work? I've tried inserting a for loop inside the where loop, but nothing worked, so I think I'm just really misunderstanding the syntax of xquery. Help?
Ideally, your XQuery processor supports group by, so the answer is fairly simple:
for $b in $books/book
let $colls-key := string-join($b/collections/collection/#c, '')
group by $colls-key
return element grouped {
$b/#isbn/string()
}
If the collections aren't always ordered in the data, you can order them before generating your key:
let $colls-key :=
string-join(
for $c in $collections/collection/#c
order by $c
return $c)
Without group by it's a little messier.
declare function local:key($collections){
string-join($collections/collection/#c, '')
};
for $key in distinct-values($doc/books/book/local:key(collections))
return element grouped {
$doc/books/book[local:key(collections) eq $key]/#isbn/string()
}
Depending on the XQuery processor that also may be less efficient, so it's probably not advisable for large data sets. If performance is an issue, then you can look into what optimizations are available for your specific implementation.

Reusing MyBatis ResultMap in multiple mapper.xml

I would like to re-use a specific from different *Mapper.xml files which all somehow read same objects.
I have a Database table called Project, which I created the following resultMap for:
<resultMap id="ProjectMap" type="com.model.Project">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="client_prj_no" jdbcType="VARCHAR" property="clientPrjNo" />
<result column="notes" jdbcType="VARCHAR" property="notes" />
<result column="start_date" jdbcType="TIMESTAMP" property="startDate" />
...
<resultMap>
It works great in the ProjectMapper.xml, however, now I want to create a ClientWithProjectsMapper.xml where I want to SELECT * FROM CLIENT, PROJECT where PROJECT.CLIENT_ID = CLIENT.ID and have a Client object return with a List objects. In other words, I want to get a ClientWithProjects with a single SQL.
In my mapping, I want to reuse the ProjectMap (without copy/paste) which I defined in the ProjectMapper.xml, but I am not sure how to accomplish this.
I could factor out the ProjectMap into a separate file, but I have not found any facilities in MyBatis to #include other files.
Any ideas on how this can be done? (I am using Maven, are there any plugins that would filter the files looking for #include or such, and include the contents of the file right into file being processed?).
Thanks.
-AP_
Once you import all the mapper xmls in mybatis-config.xml file you can refer ResultMaps configured in any of the mapper xmls using resultMap id along with namespace.
for Ex:
ProjectMapper.xml
<mapper namespace="com.mybatisapp.mappers.ProjectMapper">
<resultMap id="ProjectMap" type="com.model.Project">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<resultMap>
</mapper>
In ClientWithProjectsMapper.xml
<select id="selectProjectsByClient" parameterType="int"
resultMap="com.mybatisapp.mappers.ProjectMapper.ProjectMap">
select * from blahblah
</select>
Here you can reference resultMap in another Mapper xml files using fully qualified name as "com.mybatisapp.mappers.ProjectMapper.ProjectMap"

Missing schema in DBML if using LINQ to SP and Sp returning multiple record sets

while making of POC of LINQ to SQL and entities, i faced a problem stuck in a frozen dead end.
Problem is , am using LINQ to SP and every things was working fine and i made cool methods of editing, adding and deleting. Then some thing click in my mine that "what if i just return two record set from SP". i made a SP and returned two record set from it
SP look like this [Demo]
Create PROCEDURE [dbo].GetUserData
#UserId Bigint
AS
BEGIN
SET NOCOUNT ON;
-- Getting User
select * from [User] where id=#UserId
-- Getting User's role
select * from [Role] where userId=#UserId
end
then i droped that SP in my DBML (Linq to SQL classes) then here i noticed that only schema of one record set was created like this
<?xml version="1.0" encoding="utf-8"?>
<Database Name="MyInventory" Class="MyDBMLDataContext" xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007">
<Connection Mode="AppSettings" ConnectionString="Data Source=MyDatabaseServer\;Initial Catalog=MyInventory;Integrated Security=True" SettingsObjectName="ConsoleApplication16.Properties.Settings" SettingsPropertyName="MyInventoryConnectionString" Provider="System.Data.SqlClient" />
<Function Name="dbo.GetUserData" Method="GetUserData">
<Parameter Name="UserId" Parameter="userId" Type="System.Int64" DbType="BigInt" />
<ElementType Name="GetUserDataResult">
<Column Name="Id" Type="System.Int64" DbType="BigInt NOT NULL" CanBeNull="false" />
<Column Name="Name" Type="System.String" DbType="VarChar(50) NOT NULL" CanBeNull="false" />
<Column Name="Email" Type="System.String" DbType="NVarChar(50)" CanBeNull="true" />
<Column Name="IsDeleted" Type="System.Boolean" DbType="Bit NOT NULL" CanBeNull="false" />
<Column Name="HomePage" Type="System.String" DbType="VarChar(100)" CanBeNull="true" />
</ElementType>
</Function>
</Database>
i can clearly see that only one record set is created of Users records and it is missing Role schema :(.
Can any body tell me what and why is that so?
Thanks
Lura
I have had to deal with something similar in getting multiple data sets from a data base for a website. What we did was create an DatabaseExtensions.cs file to add the queries with multiple data sets.
So in the extensions file we would have something like this
public partial class DataBaseDataContext
{
[ResultType(typeof(FirstResult))]
[ResultType(typeof(SecondResult))]
[Function(Name = "dbo.StoredProc")]
public IMultipleResults StoredProc([global::System.Data.Linq.Mapping.ParameterAttribute(DbType = "Int")] System.Nullable<System.Int> ID)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), ID);
return ((IMultipleResults)(result.ReturnValue));
}
}
public class FirstResult;
public class SecondResult;
Note: I changed some of the names of things in this code to make it easier to read, so it may not work as is.
FirstResult and SecondResult are the result type classes. I would usually copy them from the dbml's accompanying .cs file then rename it. I didn't include their code here because it can be rather long.
DataBaseDataContext dataCon = new DataBaseDataContext();
var results = dataCon.StoredProc(id);
var firstSet = results.GetResult<FirstResult>();
var secondSet = results.GetResult<SecondResult>();
//process data
It is important to get your results out in the same order they come out in your stored procedure. After you have gotten your results out, you can use LINQ or whatever to work with them.
My finding is that if you add a result set to your dbml manually you get the results that you want but LinqToSql doesn't offer you a simple way to have access to them
Here is a path to your results
((System.Data.Linq.SqlClient.ObjectReaderCompiler.ObjectReaderSession
<System.Data.SqlClient.SqlDataReader>)
(((System.Data.Linq.SqlClient.SqlProvider.ExecuteResult)(result)).session)).buffer
you here have access to your result by index buffer[0] will return the results of your first select statement and buffer[1] returns the result of second select.
probably you can cast this two IEnumerables to typed IEnumerable<GetAllResult> and IEnumerable<GetAllResult1> although I didn't test that.
I build this sample dbml
<Function Name="dbo.GetAll" Method="GetAll">
<ElementType Name="GetAllResult">
<Column Name="ID" Type="System.Int32" DbType="Int" CanBeNull="true" />
<Column Name="Tagname" Type="System.String" DbType="NChar(10)" CanBeNull="true" />
</ElementType>
<ElementType Name="GetAllResult1">
<Column Name="Id" Type="System.Int32" DbType="Int" CanBeNull="true" />
<Column Name="TagId" Type="System.Int32" DbType="Int" CanBeNull="true" />
<Column Name="Name1" Type="System.String" DbType="NChar(10)" CanBeNull="true" />
</ElementType>
</Function>
and the generated cs file would be like this
[global::System.Data.Linq.Mapping.FunctionAttribute(Name="dbo.GetAll")]
[global::System.Data.Linq.Mapping.ResultTypeAttribute(typeof(GetAllResult))]
[global::System.Data.Linq.Mapping.ResultTypeAttribute(typeof(GetAllResult1))]
public IMultipleResults GetAll()
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())));
return ((IMultipleResults)(result.ReturnValue));
}
Have you looked code generated for your model? Two resultsets will be here. Mapping can lie.
Ramesh

Resources