uuid custom typehandler mybatis - mybatis

I want to manage a table with a VARCHAR primary key, that in the
mapped java object should be a UUID.
i have my sql-map-config.xml:
<?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>
<properties resource="database.properties"/>
<typeHandlers>
<typeHandler
handler="[...].persistence.typehandlers.UuidTypeHandler"
javaType="java.util.UUID"
jdbcType="VARCHAR"/>
</typeHandlers>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="user.xml" />
</mappers>
</configuration>
and the user.xml is like that:
<?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="[...].persistence.mappers.UserMapper">
<select id="selectUserByUUID" parameterType="java.util.UUID"
resultMap="userResultMap">
SELECT * FROM user WHERE uuid = #{uuid, jdbcType=VARCHAR,
typeHandler=[...].persistence.typehandlers.UuidTypeHandler}
</select>
<resultMap id="userResultMap" type="[...].model.User">
<id property="uuid" column="uuid" jdbcType="OTHER"
typeHandler="[...].persistence.typehandlers.UuidTypeHandler"/>
...
</resultMap>
</mapper>
anyway, i got this exception:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause:
org.apache.ibatis.reflection.ReflectionException: There is no getter
for property named 'uuid' in 'class java.util.UUID'
### The error may involve
[...].persistence.mappers.UserMapper.selectUserByUUID-Inline
### The error occurred while setting parameters
### Cause: org.apache.ibatis.reflection.ReflectionException: There is
no getter for property named 'uuid' in 'class java.util.UUID'
it seems that my typehandler never gets called (i have it logging a
bit, but never prints anything).
Is there something wrong? Thanks.

Your problem is right there in your exception....
There is no getter for property named 'uuid' in 'class java.util.UUID'
Use a parameter type of String, and pass in unique id as an argument. You don't need a type handler.

Seems kinda odd answering my own question, but i got some help on the mybatis-users list, so i'd like to share some hints here:
Mybatis was trying to "get" a non-existing field:
Luckily, one of mybatis developers helped me a while ago, suggesting that the easiest way to get it to work was to add a #Param annotation in the UserMapper to do this thing:
public User selectUserByUUID(#Param("uuid") UUID uuid);
<select id="selectUserByUUID" parameterType="uuid" resultMap="userResultMap">
SELECT * FROM user WHERE uuid = #{uuid, typeHandler=com.collective.persistence.typehandlers.UuidTypeHandler, javaType=uuid, jdbcType=VARCHAR}
</select>
I was stuck in watching the typehandler configuration, but,
if I understood it right, this has not much to do with typehandlers,
since they are used to "transport and translate" data between the sql
table and the java objects (through setter and getters), while my
issue was in the handling of methods' parameters. Hope this helps someone.

Also such error could be fixed by adding in mapper file new method with corresponding queue
Smth like this:
#Select("SELECT * FROM Users u WHERE u.userUUID = '${uuid}'")
User selectByUserUuid(#Param("uuid") UUID uuid);

Related

Mybatis generator configuration set tableName case sensitive

This is my xml configuration:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2SAMPLE" defaultModelType="flat" targetRuntime="MyBatis3">
<plugin type="org.mybatis.generator.plugins.RenameExampleClassPlugin">
<property name="searchString" value="Example$"/>
<property name="replaceString" value="Criteria"/>
</plugin>
<plugin type="org.mybatis.generator.plugins.CaseInsensitiveLikePlugin" />
<plugin type="org.mybatis.generator.plugins.RowBoundsPlugin" />
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<jdbcConnection driverClass="com.ibm.db2.jcc.DB2Driver" connectionURL="jdbc:db2://LOCALHOST:50000/mydb" userId="username" password="password">
</jdbcConnection>
<javaModelGenerator targetPackage="foo.package" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="false" />
</javaModelGenerator>
<sqlMapGenerator targetPackage="foo.package" targetProject="src/main/resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER" targetPackage="foo.package" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<table schema="SCHEMA" tableName="MyTable" alias="MYTABLE" domainObjectName="MYTABLE"/>
</context>
</generatorConfiguration>
The problem is that it not found the table MyTable because it search MYTABLE.
How can I setup MyBatis generator for searching case sensitive name MyTable?
Thanks.
From https://www.codeday.top/2016/12/22/6853.html
1, If schema,catalog or tablename There are spaces in , So what format is set , On the exact use of the specified format to query ;
2, otherwise , If the database identifier uses uppercase , that MBG Automatically turn table name into uppercase search ;
3, otherwise , If the database identifier uses lowercase , that MBG Automatically turn table name to lowercase search ;
4, otherwise , Query using the specified case format ;
Additional , If the table is created , Using "" Specify database object size , Even if the database identifier is used , In this case a table name is created using a given case size ;
At this time , Please set delimitIdentifiers="true" Case preserving format ;

mybatis <id> tag in <resultMap> behavior

I wrote mapping file like below.
<resultMap id="fooResultMap" type="Foo" >
<result column="NAME" property="name" jdbcType="VARCHAR" />
<association property="bar" resultMap="barResultMap" />
</resultMap>
When I get 2 record of same NAME colmun value, mybatis return only 1 Foo object.
I want 2 Foo object becouse bar's column is different.
So, I workaround below.
<resultMap id="fooResultMap" type="Foo" >
<id column="dummy_column_for_unique" />
<result column="NAME" property="name" jdbcType="VARCHAR" />
<association property="bar" resultMap="barResultMap" />
</resultMap>
I added <id> tag in <resultMap> and "dummy_column_for_unique" column is dummy(not exists).
I successfully got 2 Foo object.
I suppose dummy column result in null, and mybatis probably handle null value as different record.
But such behavior is not explained in manual or other resources on internet I searched.
Is there any information of this behavior ?
In mybatis's mailing list, I was recommended to use row number.
https://groups.google.com/forum/#!topic/mybatis-user/FAvrJ4piYlU
So, my final resultMap is like below.
<resultMap id="fooResultMap" type="Foo" >
<id column="rownum" />
<result column="NAME" property="name" jdbcType="VARCHAR" />
<association property="bar" resultMap="barResultMap" />
</resultMap>
And sql is like below(original sql is wraped by outer select sentence for not modifying original sql, and this sql grammar is for mysql).
select #rownum:=#rownum+1 as rownum, org.* from (
### original sql ###
) org, (select #rownum:=0) r
I hope this will help someone.
MyBatis maps the objects using the ID. If there is no ID in the ResultMap, they are all mapped in the same object.
Your table should have a primary key, but I guess that creating a fake ID as you did might serve as a workaround.

Mybatis Generator's bug: configuration items should be ordered?

If I put "<commentGenerator>" after "<jdbcConnection>", MBG proposed an error that context content should match: blablabla...
But when I put "<commentGenerator>" before "<jdbcConnection>", everything is ok. Here I have something to complain to the official website that if the order of these items is need, why you do not tell us! What an important thing! You r kidding the freshmen. Maybe it is some where I do not know, but this is a key point to build the MBG's configuration file successfully, why not put this NOTE on the top of the tutorial or somewhere eye-catching?
<generatorConfiguration >
<classPathEntry location="D:\mariadb-java-client-1.1.7.jar" />
<context id="db" >
<commentGenerator>
<property name="suppressAllComments" value="true" />
<property name="suppressDate" value="true" />
</commentGenerator>
<jdbcConnection driverClass="org.mariadb.jdbc.Driver"
connectionURL="jdbc:mariadb://localhost:3306/dbname"
userId="root"
password="password"
/>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- Model Class -->
<javaModelGenerator targetPackage="org.infrastructure.model" targetProject="infrastructure\src\main\java">
<property name="enableSubPackages" value="false" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- XML Files -->
<sqlMapGenerator targetPackage="sqlMap" targetProject="infrastructure\src\main\config">
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- DAO -->
<javaClientGenerator type="XMLMAPPER" targetPackage="org.infrastructure.dao" targetProject="infrastructure\src\main\java">
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- Tables -->
<table tableName="user" domainObjectName="User" ></table>
</context>
</generatorConfiguration>
First of all, in your xml configuration file, it doesn't contains a valid root element, which always should be like <!DOCTYPE .../>. About how to add a correct root element of mybatis generator configuration file, please see example from MyBatis GeneratorXML Configuration File Reference.
If you correctly specified root element such as following:
<!DOCTYPE generatorConfiguration PUBLIC
"-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"
>
This root element contains a typical DTD declaration located at http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd. This is the definition of the order of these items is need. And we are going to see what it looks like.
From line 47 of this document type definition, it defines element named context. The content is as following:
<!--
The context element is used to describe a context for generating files, and the source tables.
-->
<!ELEMENT context (property*, plugin*, commentGenerator?, jdbcConnection, javaTypeResolver?,javaModelGenerator, sqlMapGenerator?, javaClientGenerator?, table+)>
Which obviously defined the order of the element in context, that is:
property*, plugin*, commentGenerator?, jdbcConnection,
javaTypeResolver?,javaModelGenerator, sqlMapGenerator?,
javaClientGenerator?, table+
In this element, all children must occurs as following rules:
+ for specifying that there must be one or more occurrences of the item — the effective content of each occurrence may be different;
* for specifying that any number (zero or more) of occurrences is allowed — the item is optional and the effective content of each occurrence may be different;
? for specifying that there must not be more than one occurrence — the item is optional;
If there is no quantifier, the specified item must occur exactly one time at the specified position in the content of the element.
After we understanding its real meaning, why you could not change the order of commentGenerator and jdbcConnection should be clear.
Maybe you want to know how to make the element out of order, question How to define DTD without strict element order could be useful.
Wish it helpful.

Recommended way to add a new entity to sylius core bundle?

Currently using:
base install of sylius-standard, for an ecommerce website.
Here is what I would like to accomplish :
Basically we know there is an User Entity which resides in
vendor/sylius/sylius/src/Sylius/Component/Core/Model/User.php
This Entity is actually extending the FOS\UserBundle\Model\User and it also has a linked UserInterface which is implementing FOS\UserBundle\Model\UserInterface.
My goal is to create another entity UserData which can be linked via the Doctrine OneToMany field relation to the above sylius User Entity which is basically the table sylius_user.
So effectively User Entity linked to UserData Entity via oneToMany ( For each user there can be multiple UserData entity instances.)
Sylius documentation (http://sylius.readthedocs.org) does not have anything related to adding an entity - If I am mistaken, please guide me to the correct links.
I have read overriding models at - > sylius models override also have gone through another sort of related question on stackoverflow How to create a new model with Sylius via SyliusResourceBundle but it is not clear as he is using the SyliusResourceBundle while I want to extend the SyliusCoreBundle (not sure if we can extend it in the first place or not)
How do I achieve this in the sylius framework ?
I solved the problems by reading segregated posts, so putting in here the solution for the community (please let me know if this is not the recommended way and the correct way if not):
My goal was to create a oneToMany doctrine relationship between
Sylius\Component\Core\Model\User
And
Acme\Bundle\Entity\UserData
which I had created. Basically for every user there could be multiple userdata instances pointing back to that user.
To do this, at first I followed the docs at sylius documentation
to override the sylius user class which is infact an extended class of fosuserbundle.
At some stage I kept getting the error [Doctrine\DBAL\Schema\SchemaException]
The table with name 'astrohealing_dev.sylius_user' already exists.
Based on Winzou's suggestion : To fix this error I changed the user class locations in sylius.yml (Sylius/Bundle/CoreBundle/Resources/config/app/sylius.yml) which is part of the core bundle and declared my own class as the user model like this :
This resolved the sylius_user table exists error
But then I got another error :
[Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException]
Configuration path "sylius_core.driver" cannot be overwritten. You have to define all options for this path, and any of its sub
-paths in one configuration section.
Next I commented out drive : doctrine/orm line for the relevant entry in
app/config/config.yml
sylius_core:
# driver: doctrine/orm
classes:
user:
model: Acme\Bundle\Entity\User
sylius_resource:
resources:
sylius.userdata:
driver: doctrine/orm
classes:
model: Acme\Bundle\Entity\UserData
The above few lines also show :
The override for sylius_core with my new User class
The new resource - which is bascially just the userdata class
Now for the Doctrine Mapping (User.orm.xml):
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Acme\Bundle\Entity\User" table="sylius_user">
<one-to-many
field="userdata"
target-entity="Acme\Bundle\Entity\UserData"
mapped-by="userid" />
</entity>
</doctrine-mapping>
And the mapping for UserData (UserData.orm.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Acme\Bundle\Entity\UserData" table="sylius_userdata">
<id name="id" type="integer">
<generator strategy="AUTO" />
</id>
<many-to-one
field="userid"
target-entity="Acme\Bundle\Entity\User"
inversed-by="userdata"
join-column="userid">
<join-column name="user_id" referenced-column-name="id" nullable="false" />
</many-to-one>
<field name="name" type="string" length="150" />
<field name="datetime" type="datetime" />
</entity>
</doctrine-mapping>

Struts 2 interceptor redirect JSP page

Using Struts 2, I have this interceptor:
public class AccessInterceptor extends AbstractInterceptor {
#Override
public String intercept(ActionInvocation ai) throws Exception {
ActionContext actionContext = ai.getInvocationContext();
Map<String, Object> session = actionContext.getSession();
Boolean logged = (Boolean) session.get("logged");
if(logged == null) {
session.put("logged", false);
logged = false;
}
if (!logged) {
// If exception is here, it IS thrown, so I am sure "login" is returned on /homepage
return "login";
}
return ai.invoke();
}
}
And this struts.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="loginInterceptor" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="access"
class="logininterceptor.interceptors.AccessInterceptor"/>
<interceptor-stack name="appDefault">
<interceptor-ref name="access" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="appDefault" />
<global-results>
<result name="login">/WEB-INF/content/login.jsp</result>
</global-results>
</package>
</struts>
I have also /WEB-INF/content/homepage.jsp JSP and /WEB-INF/content/login.jsp and mypackage.LoginAction.java class. There is not an acton class for homepage
The problem is, that when I go to the /homepage page, it is NOT redirected to login.jsp and I see the homepage.jsp content. What is wrong?
PS: I use struts2-convention-plugin
This is happening because the conventions plugin uses the "convention-default" package which does not know about your interceptor or interceptor stack.
If you could create a package which extends conventions-default doing just what your interceptor stack does (defines the interceptor, interceptor stack and defines the default result type) and then make the conventions plugin apply that stack as the default for all actions wouldn't that be great? You can.
After the <struts> tag define a constant like so:
<constant name="struts.convention.default.parent.package" value="loginInterceptor"/>
The other suggestion, was to use struts.xml to override the conventions, which is fine but a little verbose.
Another option is to use the parent package annotation, which is good when you need to override the default behavior but as you pointed out you want this to be the default behavior so you should reserve annotations for overriding this, not to implement it.
It may be worth browsing the following list of struts2 conventions plugin constants for the future: http://struts.apache.org/2.1.8/docs/convention-plugin.html#ConventionPlugin-Overwritingpluginclasses
PS: Just naming preference but I would call your package login-package rather than loginInterceptor... not a big deal.
If you want to force to execute the interceptor every time anyone is accessing to homepage.jsp, the correct way of doing this is to put an action that returns the jsp. For example:
<struts>
...
<package name="loginInterceptor" namespace="/" extends="struts-default">
<interceptors>
...
</interceptors>
<default-interceptor-ref name="appDefault" />
<global-results>
...
</global-results>
<action name="homepage">
<result>/WEB-INF/content/homepage.jsp</result>
</action>
</package>
</struts>
After that, when a user tries to access to http://.../homepage.action, the interceptor will be executed and will be redirected to the login page if the user is not logged in.