Liquibase- Creating index with descending ordering in column issue - scripting

I am Facing Error :
attribute 'descending' is not allowed to appear in element 'column'
While running liquidate (3.5) script for creating index :
please find below code :
<createIndex unique="true" indexName="EMPIDX" tableName="Employee">
<Column name="JoiningDate" descending="true" />
<Column name="Empnumber" />
</createIndex>
also I tried :
<Column name="JoiningDate DESC" />
but this give me error : column does not exist :(

I was able to run this code after making a small fix: change Column to column (change from upper case C to lower case c):
<createIndex unique="true" indexName="EMPIDX" tableName="Employee">
<column name="JoiningDate" descending="true" />
<column name="Empnumber" />
</createIndex>

Related

Liquibase loadData as string, not CLOB resource

The Problem
I recently upgraded Liquibase to 3.6.2 from 3.4.2.
Loading seed data from a CSV into text fields now results in a CLOB resource error. Before it would simply insert the text as a value.
The Setup
I'm using Liquibase to manage migrations of my data.
I have a table with an code and description column. description is of type TEXT.
<changeSet author="" id="create-table-degrees">
<createTable tableName="degrees">
<column name="code"
type="varchar(2)">
<constraints primaryKey="true"/>
</column>
<column name="description"
type="text">
<constraints unique="true"/>
</column>
</createTable>
<rollback>
<dropTable tableName="degrees"/>
</rollback>
</changeSet>
I have seed data in a CSV:
code,description
"D1","MASTERS"
"D2","DOCTORATE"
I load it using loadData:
<changeSet author="" id="seed-degrees">
<loadData file="seeds/degrees.csv"
tableName="degrees" />
</changeSet>
The Error
Unexpected error running Liquibase: CLOB resource not found: MASTERS
The Question
Is there a way to keep Liquibase from interpreting seed values as file paths instead of strings, or do I need to manually define the column types as String in loadData.
e.g. I would like to avoid having to modify the old changeSet to:
<changeSet author="" id="seed-degrees">
<loadData file="seeds/degrees.csv"
tableName="roles">
<column name="description" type="string" />
</loadData>
</changeSet>
The workaround listed in CORE-3287: Anver S December 3, 2018, 3:07 PM
While adding an explicit column type definition as defined in original
stackoverflow post
<column name="description" type="string" />
does the trick - for me it effectively requires to update already
applied changesets which ideally I'd try to avoid.

Liquibase - insert rows with uuid

I have two tables declared as follows:
<changeSet author="istvan" id="country-table-changelog">
<createTable tableName="country">
<column name="id" type="uuid">
<constraints nullable="false" unique="true" />
</column>
<column name="name" type="varchar">
<constraints nullable="false" unique="true" />
</column>
</createTable>
</changeSet>
<changeSet author="istvan" id="region-table-changelog">
<createTable tableName="region">
<column name="id" type="uuid" >
<constraints nullable="false" unique="true" />
</column>
<column name="country_id" type="uuid">
<constraints nullable="false" />
</column>
<column name="name" type="varchar">
<constraints nullable="false" unique="true" />
</column>
</createTable>
</changeSet>
<changeSet author="istvan" id="region-country-foreign-key-constraint">
<addForeignKeyConstraint
baseTableName="region"
baseColumnNames="country_id"
referencedTableName="country"
referencedColumnNames="id"
constraintName="fk_region_country"
onDelete="CASCADE"
onUpdate="RESTRICT"/>
</changeSet>
I want to fill both tables from liquibase changelog file with some values like:
INSERT INTO country VALUES('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'HUNGARY');
INSERT INTO region VALUES('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'Baranya');
In the example I used aaaa's and bbbb's just because of simplicity. I want to generate those UUID's by the DBMS.
What is the best way to do it? Do I have to use SQL in my changelog files or is it possible with XML? I prefer DBMS independent solution like XML or JSON.
My second question is that how can I declare a column with UUID that creates the UUID on insert. Something like:
<column name="id" type="uuid" value="??? GENERATE UUID ???">
<constraints nullable="false" unique="true" />
</column>
Thank you for your time!
You can do this by using properties that are defined depending on the current DBMS.
<property name="uuid_type" value="uuid" dbms="postgresql"/>
<property name="uuid_type" value="uniqueidentifier" dbms="mssql"/>
<property name="uuid_type" value="RAW(16)" dbms="oracle"/>
<property name="uuid_function" value="uid.uuid_generate_v4()" dbms="postgresql"/>
<property name="uuid_function" value="NEWID()" dbms="mssql"/>
<property name="uuid_function" value="sys_guid()" dbms="oracle"/>
Then use those properties when defining the table:
<column name="id" type="${uuid_type}" defaultValueComputed="${uuid_function}">
<constraints nullable="false" unique="true" />
</column>
Note that you need to use defaultValueComputed, not value
If the column is defined with a default value, just leave it out in your insert statements and the database will then generate the UUID when inserting.
For MySQL, put your property just before changeSet tag:
<property name="u_id" value="uuid()" dbms="mysql"/>
then
<column name="ID" type="varchar(255)" valueComputed="${u_id}"/>
NOTE: here valueComputed is used, not defaultValueComputed.
Some databases supports UUID columns: Generate UUID values by default for each row on column of UUID type in H2 Database Engine
I don't think that Liquibase has embedded UUID generator, have a look at defaultValueComputed/valueComputed property for column (http://www.liquibase.org/documentation/column.html) + DB function to generate UUID
Like Rammgarot noted, since we are dealing with columns that need to have unique values, we should use valueComputed instead of defaultValueComputed.
Any of the provided answers didn't help me in case of MySQL, but I was able to work it out using another approach - using triggers.
<changeSet author="nberezovski" id="1">
<createTable tableName="test">
<column name="id" type="varchar(255)">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="description" type="varchar(255)">
...
</createTable>
</changeSet>
<changeSet author="nberezovski" id="2">
<sql splitStatements="false">
CREATE TRIGGER insert_test
BEFORE INSERT ON test
FOR EACH ROW
BEGIN
IF (NEW.id IS NULL) THEN
SET NEW.id = UUID();
END IF;
END;
</sql>
</changeSet>
After that, each manual insert only of values for description MySQL automatically generated an id for me. For example:
insert into test(description) values('some description');
Also that approach helped in loading data using loadData
For SQL SERVER works
<column name="uuid" valueComputed="newid()" />
For me helped this (postgres 14.2):
<property name="uuid_function" value="gen_random_uuid()" dbms="postgresql"/>
<column name="id" valueComputed="${uuid_function}"/>

NHibernate filter on property from many-to-one

I'm trying to use an NHibernate filter to filter on a parent class's property, but I keep getting an error that the multi-part identifier "Parent.Active" could not be bound. My filter-def is:
<filter-def name="useActive" />
My parent class has this in the mapping:
<property name="Active">
<column name="ACTIVE" not-null="true" />
</property>
<bag name="Children" table="CHILDREN" inverse="true">
<key>
<column name="PARENT_ID_IN_CHILD" />
</key>
<one-to-many class="ChildType" />
</bag>
My child class has this in the mapping:
<many-to-one name="Parent" class="ParentTyle">
<column name="PARENT_ID_IN_CHILD" />
</many-to-one>
<filter name="useActive" condition="Parent.Active = 1" />
How can I get NHibernate to check the parent column when filtering?
edit: I'm enabling the filter using mySession.EnableFilter("useActive");. I'm also using LINQ-to-NHibernate, but I don't think that should matter.
The error you're receiving came from SQL Server that can't find the column "Active" on the table "Parent".
Keep in mind that when you're defining a filter the string that you put in the condition will simply be appended in the where condition of the select as is. If you want to filter for that field you must first identify the alias nHibernate use for your SQL query than put that alias instead of "Parent". it coube something like "mytable_0" or something like that.

nhibernate: Class Mapping with no table

I have an entity class that I use to represent the results of an sql-query. The mapping for the class shown below. Yet as far as I can tell nhiberate treats the mapping as if there is a real database table - when in fact there is not. In this case there is nothing in the database that represents this entity at all. I am using this to map a query through, but the same would be true of a view. Is there no way to indicate to nhibernate that there isn't a table represented by the mapping?
<class name="Models.UserTransaction"> <!-- Defaults table name same as Entity even though table doesn’t exist -->
<id name="Id">
<column name="Id" not-null="true" unique="true" />
<generator class="native" />
</id>
<property name="TransType" />
<property name="Date" />
<property name="Amount" />
<property name="Balance" />
</class>
This is the query I am mapping, which uses a user defined table. I couldn't get it working without having a mapping even though the example I copied appeared to.
<sql-query name="UserTransactions">
<query-param name="userId" type="string" />
<return class="Models.UserTransaction" alias="userTx">
<return-property name="TransType" column="TransType" />
<return-property name="Id" column="Id" />
<return-property name="Date" column="TransDate" />
<return-property name="Amount" column="Amount" />
<return-property name="Balance" column="Balance" />
</return>
<![CDATA[
SELECT userTx.[Type] as TransType, userTx.[Id] as Id, userTx.[Date] as TransDate, userTx.[Amount] as Amount, userTx.[Balance] as Balance
FROM dbo.User_AccountStatement(:userId) userTx
]]>
</sql-query>
If you have a db view, you can use nhibernate to map to that, but if all you are doing is storing the projection fields of the query there doesn't need to be a map at all.
How are you querying this data?
if you are using the criteria API, you can use the resultstransformer to map the returned object array to your class. the types have to match between your class that the projection.
if you are using the linq provider you can project directly into your class. so you'd have something like this
from s in Session.Query
where s.some-property== "some-value"
select new your-type
{
some-property-on-your-type = s.some-property,
some-other-property-on-your-type = s.some-other-property
}
There is no need to write a mapping to the database since you aren't mapping to an object in the database.
I guess you should at least specify a view as the tablename of your mapping.
The view should have the same resulting columns as your query (and hopefully return any row that your query could return)
Then you will be able to :
map your properties to your view/SQL query result columns
set your class as mutable="false"
declare your query as a named query (see : http://ayende.com/blog/3948/nhibernate-mapping-named-queries-query-and-sql-query )

NHibernate: Where clause on one-to-many relationships doesn't work when column name is ambiguous

It is possible to specify an arbitrary SQL where clause for collection mappings. For example:
<map name="myEntity" where="foo = 1" />
However if the column name is ambiguous for some reason, the sql fails. For example, this can occur if you are trying to use joins for example.
Given that the table aliases are automatically generated, you can't qualify the column name. This makes the feature seem rather silly. Does anyone know if there is a work around?
NHibernate should figure out the correct alias for the property you are referencing. Is foo a mapped property of the item entity type (the item type that is in the map collection) ?
For example this works:
<class name="Category" table="Category">
<id name="Id">
<generator class="guid.comb" />
</id>
<property name="Name" not-null="true" length="255" />
<bag name="ProductList" table="Product" cascade="none" where="Name like '%test%'" fetch="join">
<key column="CategoryId" />
<one-to-many class="Product" />
</bag>
</class>
There is a property on both Category and the Product class named "Name" but nhibernate will in this case use the on defined on the Product class.