MyBatis does not resolve Inner class when parsing SQL Mapper Configuration handlers - mybatis

Using MyBatis 3.2.8, I'm trying to map an enum type (Status) to the jdbc VARCHAR type (to can use only the enum in my entity bean). So I defined the TypeHandler UserStatusHandler
import com.sample.User.Status;
import org.apache.ibatis.type.EnumTypeHandler;
public class UserStatusHandler extends EnumTypeHandler<Status>
{
public UserStatusHandler(Class<Status> type)
{
super(type);
}
}
I correctly declared the handler in the xml config file and in the UserDao.xml (mapping the attribute Status to the VARCHAR in the resultMap ...)
sample :
In the XML config file:
<typeHandlers>
<typeHandler handler="com.sample.dao.UserStatusHandler" javaType="com.sample.User.Status"/>
</typeHandlers>
In the DAO mapper XML file:
<resultMap id="UserResultMap" type="User">
<id property="id" column="ID" javaType="long"/>
<result property="status" column="STATUS" typeHandler="com.sample.dao.UserStatusHandler" javaType="com.sample.User.Status"/>
xxxxx
</resultMap>
But the problem incoming from MyBatis was that MyBatis cannot found my java enum class because it is defined inside another interface
public interface User
{
public enum Status
{
A, B, C
}
...
}
When I define this enum class in a separate file, it works with no problem, but I don't like to change my design (because of a limit ? ), I search to understand why MyBatis cannot found the class in this case ? Is it a way to fix this ?
MyBatis cannot build the SqlSession. While executing a simple test to find a User I get the following exception
Cause: org.apache.ibatis.builder.BuilderException:
Error parsing SQL Mapper Configuration. Cause:
org.apache.ibatis.builder.BuilderException: Error resolving class.
Cause: org.apache.ibatis.type.TypeException: Could not resolve type
alias 'com.sample.User.Status'.
Cause: java.lang.ClassNotFoundException: Cannot find class:
com.sample.User.Status

Finally I solved it by writing, in the XML configuration file, the name of the Inner enum like its associated compiled file name in the jar i.e. by adding a dollar $ between the enveloped and the inner class.
com.sample.User$Status
It appears a bug or a limit in MyBatis ..

Related

Constructor list/array type parameters in the XML configuration

How can I pass an array/list parameter to the class constructor using the XML configuration?
If I want to pass a scalar value I use following configuration:
<components name="0">
<type>XXX, XXX</type>
<services name="0" type="XXX, XXX" key="YYY"/>
<injectProperties>true</injectProperties>
<instanceScope>single-instance</instanceScope>
<parameters>
<param1name>param1value</param1name>
<param2name>param2value</param2name>
</parameters>
</components>
The constructor of my class expects a parameter of the IEnumerable type .
Best regards,
arnam
Short version: That's not currently supported. Related issue here.
I've tried with a JSON config but parameters arn't passed to the constructor. My config looks like that:
"parameters":{
"constructorParamName":["stringValue1", "stringValue2"]
}
and the constructor is:
public MyClassConstructor(IList<string> constructorParamName = null)
Unfortunately "constructorParamName" value is always null.
Update:
I figured out that problem is caused by IContainer.ResolveNamed method. This method passes properties from a config file but doesn't passes constructor parameters.

How to set arrays of string to #EnableJpaRepositories from property files

I have a jpa configuration file with #EnableJpaRepositories annotaion. I set this annotaion value from application.properties file like this :
#EnableJpaRepositories("${jpa.repository.packages}")
public class JPAConfiguration {
....
}
and here is my application.properties file:
jpa.repository.packages=com.epms.model
and it works perfect. but i want to specify multiple packages for #EnableJpaRepositories . so i changed my config file to this :
jpa.repository.packages=com.epms.model,com.ecms.model
and also configuration file to this :
#EnableJpaRepositories("#{'${jpa.repository.packages}'.split(',')}")
public class JPAConfiguration {
}
but it's not working . any idea ? how can i do this in my configuration file?
As #amicoderozer is asking, if your classes share a common base package you only must indicate that root package.
If it's not your case (despite you are loading from a config file or you are declaring them manually) maybe the problem (will help posting any Exception or Runtime trace) is the way the split method is used. It returns an array, and I guess the generated code will be like this:
#EnableJpaRepositories("jpa.repository.packages1","jpa.repository.packages2")
That code doesn't compile.
Never tried Spring EL inside the annotation of a component, but despite this, maybe you should indicate the basePackages this way:
#EnableJpaRepositories(basePackages = "#{'${jpa.repository.packages}'.split(',')}")
If doesn't work, I recomend you first test it by manual array declaration:
#EnableJpaRepositories(basePackages = { "com.epms.model","com.ecms.model" })
Be sure all works as you expect, and then try again reading and parsing from config file.
UPDATE:
After some readings, I've concluded that is not possible do what you want. The SpEL is allowed in many places but for annotations there is only documentation and working examples with #Value annotation.

How can I stop EF detecting changes to my database / model

I have the following in my code:
DbContext = new DataContext();
DbContext.Configuration.AutoDetectChangesEnabled = false;
Then in my controller method:
// GET /api/Applications
public IEnumerable<Application> Get()
{
return _uow.Applications.GetAll().OrderBy(p => p.Name);
}
However I still get the following message:
System.InvalidOperationException was unhandled by user code
HResult=-2146233079
Message=The model backing the 'DataContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269).
Source=EntityFramework
StackTrace:
Can someone explain why this is happening. I thought the line after my context creation would stop this check.
You can place (it's a static method)
Database.SetInitializer<DataContext>(null);
before you first use your DataContext. But take a note, that this will turn it off. Done. If your database is not compatible with model, your queries etc. will fail.
This is because you have some difference in database schema with your entity mode. The error is saying that you need to update the database schema according to the new model you have in the code.
AutoDetectChangesEnabled is not something related to this error. Because it is about tracking changes to the data while the error you are getting is related to the changes in database schema .
The error message is suggesting you to use code first migrations to update the database schema according to your changes in data model.
You have 2 options:
1. Set the Initializer of your context to null by placing this line in the constructor of your derived DbContext:
Database.SetInitializer<MyContext>(null);
2. Disable the Database Initializer in your Web.config file by adding a Key inside the appSettings node:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="DatabaseInitializerForType YourNamespace.YourDbContext, YourAssemblyName"
value="Disabled" />
</appSettings>
</configuration>
Replace YourNamespace.YourDbContext and YourAssemblyName with the corresponding value in your application.

Is there a way to change connection string in database first?

I have several databases, the schema of them are same. When I use database-first, the connection string is specified when I create the edmx file. I want to know, is there a way to change the connection string? This is, so I can choose which database to operate.
We do not store connection strings in our web.configs, so the accepted solution would not work for us. If you simply attempt to provide the connection string via the base DbContext constructor, you'll get the following exception:
Code generated using the T4 templates for Database First and Model First development may not work correctly if used in Code First mode. To continue using Database First or Model First ensure that the Entity Framework connection string is specified in the config file of executing application. To use these classes, that were generated from Database First or Model First, with Code First add any additional configuration using attributes or the DbModelBuilder API and then remove the code that throws this exception.
To resolve this, create a partial class of your context as follows and format your connection string with the additional EF metadata (where MyContext is your context model name (e.g. your model name is MyModel.edmx, than the MyContext in code below is replaced with MyModel with all three extensions .csdl, .ssdl, .msl used)):
public partial class MyContext
{
public MyContext(string connStr)
: base(string.Format(#"metadata=res://*/MyContext.csdl|res://*/MyContext.ssdl|res://*/MyContext.msl;provider=System.Data.SqlClient;provider connection string='{0}'", connStr))
{
}
}
Change the connection string in the web.config file.
<connectionStrings>
<add name="SandBoxEntities" connectionString="metadata=r... />
</connectionStrings>
I abbreviated the actual connection string because it isn't important -- just wanted to give you an idea of what to look for in the web.config file.
You can also change your connection strings programatically. Check out Example 16.2. Programmatically modifying an EntityConnectionString.
You can define multiple connection string in web.config and then use them in your code perhaps your job.
for example:`
<connectionStrings>
<add name="conStr1" connectionString="metadata=r... />
</connectionStrings>`
<connectionStrings>
<add name="conStr2" connectionString="metadata=r... />
</connectionStrings>`
and so on
and your context class constructor get connection string name as parameter:
public MyContext(string connStr)
: base(connStr) { }
Ok. now you can use in your code as below:
using (var db = new MyContext("name=conStr1"))
{
//your code here
}
and then
using (var db = new MyContext("name=conStr2"))
{
//your code here
}

problem with open jpa

I am getting following error on console when using open jpa.What may be the possible cause?I cant post the code as its against my company policies.
[12/31/10 14:54:13:279 GMT+05:30] 00000063 MetaData W CWWJP9991W: openjpa.MetaData: Warn: OpenJPA cannot map field "abc.xyz" efficiently. It is of an unsupported type. The field value will be serialized to a BLOB by default.
[12/31/10 14:54:13:295 GMT+05:30] 00000063 MetaData W CWWJP9991W: openjpa.MetaData: Warn: The class "pqr.xyz" listed in the openjpa.MetaDataFactory configuration property could not be loaded; ignoring.
[12/31/10 14:54:13:295 GMT+05:30] 00000063 MetaData W CWWJP9991W: openjpa.MetaData: Warn: OpenJPA cannot map field "pqr.xyz" efficiently. It is of an unsupported type. The field value will be serialized to a BLOB by default.
Failed to create Bundle in DB:
<openjpa-1.2.2-SNAPSHOT-r422266:778978M-OPENJPA-975 fatal user error> org.apache.openjpa.persistence.ArgumentException: Field "abc.xyz" cannot declare that it is mapped by another field. Its mapping strategy (org.apache.openjpa.jdbc.meta.strats.HandlerFieldStrategy) does not support mapping by another field.
at org.apache.openjpa.jdbc.meta.strats.AbstractFieldStrategy.assertNotMappedBy(AbstractFieldStrategy.java:59)
at org.apache.openjpa.jdbc.meta.strats.HandlerFieldStrategy.map(HandlerFieldStrategy.java:71)
at org.apache.openjpa.jdbc.meta.FieldMapping.setStrategy(FieldMapping.java:121)
at org.apache.openjpa.jdbc.meta.RuntimeStrategyInstaller.installStrategy(RuntimeStrategyInstaller.java:80)
at org.apache.openjpa.jdbc.meta.FieldMapping.resolveMapping(FieldMapping.java:454)
at org.apache.openjpa.jdbc.meta.FieldMapping.resolve(FieldMapping.java:419)
at org.apache.openjpa.jdbc.meta.ClassMapping.resolveNonRelationMappings(ClassMapping.java:879)
at org.apache.openjpa.jdbc.meta.MappingRepository.prepareMapping(MappingRepository.java:339)
at org.apache.openjpa.meta.MetaDataRepository.preMapping(MetaDataRepository.java:662)
at org.apache.openjpa.meta.MetaDataRepository.resolve(MetaDataRepository.java:549)
at org.apache.openjpa.meta.MetaDataRepository.getMetaData(MetaDataRepository.java:308)
at org.apache.openjpa.meta.MetaDataRepository.getMetaData(MetaDataRepository.java:363)
at org.apache.openjpa.kernel.QueryImpl.classForName(QueryImpl.java:1569)
at org.apache.openjpa.kernel.ExpressionStoreQuery$1.classForName(ExpressionStoreQuery.java:108)
at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.getClassMetaData(JPQLExpressionBuilder.java:168)
at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.resolveClassMetaData(JPQLExpressionBuilder.java:139)
at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.getCandidateMetaData(JPQLExpressionBuilder.java:225)
at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.getCandidateMetaData(JPQLExpressionBuilder.java:195)
at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.getCandidateType(JPQLExpressionBuilder.java:188)
at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.access$600(JPQLExpressionBuilder.java:69)
at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder$ParsedJPQL.populate(JPQLExpressionBuilder.java:1756)
at org.apache.openjpa.kernel.jpql.JPQLParser.populate(JPQLParser.java:56)
at org.apache.openjpa.kernel.ExpressionStoreQuery.populateFromCompilation(ExpressionStoreQuery.java:153)
at org.apache.openjpa.kernel.QueryImpl.newCompilation(QueryImpl.java:658)
at org.apache.openjpa.kernel.QueryImpl.compilationFromCache(QueryImpl.java:639)
at org.apache.openjpa.kernel.QueryImpl.compileForCompilation(QueryImpl.java:605)
at org.apache.openjpa.kernel.QueryImpl.compileForExecutor(QueryImpl.java:667)
at org.apache.openjpa.kernel.QueryImpl.compile(QueryImpl.java:574)
at com.xyz.ws.persistence.EntityManagerImpl.createNamedQuery(EntityManagerImpl.java:105)
at com.xyz.ws.persistence.EntityManagerImpl.createNamedQuery(EntityManagerImpl.java:37)
at com.xyz.ws.jpa.management.JPATxEmInvocation.createNamedQuery(JPATxEmInvocation.java:116)
at com.xyz.ws.jpa.management.JPAEntityManager.createNamedQuery(JPAEntityManager.java:302)
I just solved an error like this. The error was quite misleading. The problem was I forgot to put the entity into the persistence.xml file.
I was having the same problem.
The message is not clear!
What solved my problem was implementing equals() and hashCode() in the entity object and its pk object.
Also see if the class is declared inside the persistence.xml
Hope it helps someone.
I'm going to guess (best I can do without seeing code) that you have a relationship to another class that you have failed to annotate with #ManyToOne, #OneToMany or similar.