I have a Card entity class with many columns.
I used dto because I want to get some columns from this Entity.
I created a DTO class and wrote a query to CardRepositoryCustom.
But when I try to run the Query I get various errors "No constructor taking".
My dto class:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class CardDTO {
private String test1;
private String test2;
private String test3;
private String test4;
}
JPQL RepositoryCustom :
#Repository
#Transactional
public class CardRepositoryCustom {
public CardDTO test1() {
JpaResultMapper jpaResultMapper = new JpaResultMapper();
String q = "SELECT "+
" c.test1 "+
"FROM "+
" CardEntity c ";
Query query = entityManager.createQuery(q);
try {
return jpaResultMapper.uniqueResult(query, CardDTO.class);
} catch (NoResultException nre) {
return null;
}
}
}
Errors:
java.lang.RuntimeException: No constructor taking: java.lang.String
at org.qlrm.mapper.JpaResultMapper.findConstructor(JpaResultMapper.java:107) ~[qlrm-1.7.1.jar:na]
at org.qlrm.mapper.JpaResultMapper.uniqueResult(JpaResultMapper.java:64) ~[qlrm-1.7.1.jar:na]
at ....
I know how to fix this error.
You just need to write the following constructor in the DTO class as shown below.
public CardDTO(String test1) {
this.setTest1(test1);
}
Here I am curious, if I want test2, test3, test4 and write Query in Custom class, should there be as many constructors in DTO class as that number?
I think this method is really inefficient. Is there any solution I am not aware of?
This seems to be more about QLRM than JPA or Spring.
Judging from the error message the constructor has to match the arguments provided. Probably the easiest variant is to always provide all arguments, possibly with a value of null or some other literal.
The query in your example would then become.
SELECT c.test1, null, null, null FROM CardEntity c
Of course QLRM just provides a JpaResultMapper and it would probably not too difficult to provide your own, or offer a PR to QLRM that inspects names of the result set and tries to match them to parameter names or the constructor.
Related
I have this camel route:
from("direct:getUser")
.pollEnrich("jpa://User?namedQuery=User.findById&consumeDelete=false");
This is my user Entity:
#Entity
#NamedQueries({
#NamedQuery(name="User.findAll", query="SELECT u FROM User u"),
#NamedQuery(name="User.findById", query="SELECT u FROM User u WHERE u.id = :id")
})
public class User{
#Id
private String id;
}
I have tried this route by setting the header:
from("direct:getUser")
.setHeader("id", simple("myid"))
.pollEnrich("jpa://User?namedQuery=User.findById&consumeDelete=false");
But it is not working
Is there any method to set jpa properties by the headers? The camel documentation quote this in parameters option but i don't found the examples
Options: parameters
This option is Registry based which requires the # notation. This
key/value mapping is used for building the query parameters. It is
expected to be of the generic type java.util.Map where
the keys are the named parameters of a given JPA query and the values
are their corresponding effective values you want to select for. Camel
2.19: it can be used for producer as well. When it's used for producer, Simple expression can be used as a parameter value. It
allows you to retrieve parameter values from the message body header
and etc.
I hope it's not too late to answer. In any case I had a similar issue in my project, the client does a HTTP GET with a parameter id, which is used by the JPA query and the result is finally marshalled back to the HTTP client. I'm running camel in a Spring application.
I finally figured out how to achieve it in a reasonably clean way.
This is the RouteBuilder where the route is defined:
#Override
public void configure() throws Exception {
Class dataClass = SomeClass.class;
JacksonDataFormat format = new JacksonDataFormat();
format.setUnmarshalType(dataClass);
String jpaString = String
.format("jpa://%1$s?resultClass=%1$s&namedQuery=q1" +
"¶meters={\"id\":${headers.id}}", dataClass.getName());
from("jetty://http://localhost:8080/test").toD(jpaString) // note the .toD
.marshal(format)
}
And this is the StringToMapTypeConverter class, otherwise camel cannot convert {"id": X} to a map
public class StringToMapTypeConverter implements TypeConverters {
private static final ObjectMapper mapper = new ObjectMapper();
private static JavaType mapType;
static {
mapType = mapper.getTypeFactory().constructMapType(Map.class,
String.class, Object.class);
}
#Converter
public Map<String, Object> toMap(String map) throws IOException {
return mapper.readValue(map, mapType);
}
}
Remember to add it to the context. In Spring is something like:
<bean id="myStringToMapTypeConverter" class="....StringToMapTypeConverter" />
Refs:
http://camel.apache.org/jpa.html
http://camel.apache.org/message-endpoint.html#MessageEndpoint-DynamicTo
http://camel.apache.org/type-converter.html#TypeConverter-Addtypeconverterclassesatruntime
I am trying to retrieve a USER from my database using the ID in the WHERE clause. However I am receiving an error and my program is failing.
This is the error I'm receiving when I run my program:
ERROR [org.jboss.as.ejb3.invocation] (default task-19)
JBAS014134: EJB Invocation failed on component CustomerServiceBeanImpl
for method public abstract package.name.entity.ICustomer
package.name.bean.CustomerServiceBean.getCustomerById(long):
javax.ejb.EJBException: java.lang.IllegalArgumentException:
Parameter value [19533] did not match expected type [package.name.entity.User (n/a)]
Note: [19533] is a test value I used.
This is the method that is having the error in the CustomerServiceBeanImpl.java:
#Override
public Customer getCustomerById (final long id)
{
return Customer.getById (this.em, id);
}
This is the method that's being called by the component CustomerServiceBeanImpl:
public static Customer getById (final EntityManager em, final long id)
{
for (final Customer c : em.createNamedQuery ("Customer.getById", Customer.class)
.setParameter ("id", id).setMaxResults (1).getResultList ())
{
return c;
}
return null;
}
The name query is this:
#NamedQuery (name = "Customer.getById",
query = "SELECT o FROM gnf.Customer o WHERE o.user = :id")
In the Customer.java class itself the relevant column is this one:
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn (name = "user_id")
private User user;
Doing a quick check of my ERD the "id" column in my "customer" table has a data type of BIGINT. However I'm not sure if that matters. (PostgreSQL database by the way.)
How can I fix this error?
The WHERE clause in your named query seems to be the problem. The attribute user in your Customer.class is of type User and your query expects it to be a type compatible to long.
...
Parameter value [19533] did not match expected type [package.name.entity.User ...
So if you need more help on this it would be great to see the complete entities User and Customer.
It is happening because in your database the parameter will be a #oneToOne object and you have to call the id inside the Object so you have to give the query as following :
"select user from User user where user.customer.id=:param"
I use code first approach in my project. In the prject I have classes with MetadataType attribute, which I don't use in my EF model. Class with metadata has some constant public fields (in addition to fields from main type). Now when I tried to query EF it threw exception that in metadata class there fields not mapped... se below in details
class M1
{
int Id;
string Name
}
class M2
{
int Id;
DateTime Date
}
[MetadataType(typeof(PageOFSRevenueMetadata))]
class NotRelatedToModel
{
int Prop1;
int Prop2;
}
class PageOFSRevenueMetadata
{
public const string RuleSet1 = "r1";
public const string RuleSet2 = "r2";
// Data Vaidation Attrs...
int Prop1;
int Prop2;
}
In my context I have mapping only for M1 and M2. In the DB exists table with name 'NotRelatedToModel', but I don't want to use it in my model. I use EF 6
Now when I try to make join query on M1 and M2 it threw below exception
'NotRelatedToModel' contains the following unknown properties or fields: RuleSet1, RuleSet2. Please make sure that the names of these members match the names of the properties on the main type
I can move this static fields to another place and it seems to work, but I would like to know why it is happend ? How the EF code first mapping works ?
Thanks in advance
I'm trying to query abstract entity using SqlQuery() method (code first).
public abstract class UserComment
{
... [internals]
}
public class BlogComment : UserComment
{
... [internals]
}
var result = Context.Database.SqlQuery<UserComment>(
#"select * from [UserComments] where ... [internals]",
new SqlParameter("user_id", user.Id));
This gives me error:
System.ArgumentNullException: Value cannot be null.
Parameter name: constructor
If I change abstract type to concrete..
Context.Database.SqlQuery<BlogComment>
...everything works fine.
Is it possible to query abstract class using raw queries?
I didn't try it but I expect the answer is no. You cannot create instance of abstract class and that is exactly what EF is trying to do when materializing result set of the raw query.
The version of Playframework is 1.2.x,and I want to transform the query.ResultList to VO.
I created a Part entity bean as below:
#Entity
#Table(name="evaluation_part")
public class Part extends Model {
public String name;
public String collegeName;
public int peopleNum;
}
The data:
id name collegeName peopleNum
1 Jsj1 JJJJ 32
2 Jsj2 JJJJ 23
3 Jsj3 JJJJ 32
4 Tjb1 TTTT 11
5 Tjb2 TTTT 14
6 Tjb3 TTTT 16
My value object class:
public class PartVO {
public String collegeName;
public int peopleNum;
}
And I want to use the native query to get the result:
String sql="select collegeName,SUM(peopleNum) as peopleNum from evaluation_part group by collegeName";
The query result is:
collegeName peopleNum
TTTT 41
JJJJ 87
I tried:
String sql="select collegeName,SUM(peopleNum) as peopleNum from evaluation_part group by collegeName";
Query query =JPA.em().createNativeQuery(sql);
List<PartVO> partVOs = query.getResultList();
for(int i=0;i<partVOs.size();i++) {
System.out.println(partVOs.get(i).collegeName);
}
Following error is what i am getting
ClassCastException occured : [Ljava.lang.Object; cannot be cast to valueobject.PartVO
You don't have to user raw sql to do that. With hql you can use the new operator to create your VO (see http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html#queryhql-select)
You have to define a two arg constructor in your partVO class, then you can do
select new package.PartVO(collegeName, SUM(peopleNum)) from Part group by collegeName
Solution 1:Only use 'select new Part()'(constructor defined in the Part class) in the HQL that u can convert object to Part.Hibernate use reflection to automatically inject all the fields u need.
Solution 2:Here the returned type of result must be Object[],so that u can got every field of the record fetched from database by the index of array;
The difference between solution1 and solution2:the previous use constructor in the query and the later transform a record into Object[].
In your case,ignore the complex relationship between entities,solutions above will make work.
Referenced Code here:
package controllers;
import play.*;
import play.db.jpa.JPA;
import play.mvc.*;
import java.util.*;
import models.*;
/**
* This demo is intended for fetching data from MYSQL.
* #author dhl#oopsplay.org
*/
public class Application extends Controller {
public static void index() {
render();
}
/**
* Prepare some data to test.
*/
public static void addPart() {
//Add a part record to database.
Part newPart=new Part("software","zjut",8).save();
if(newPart.isPersistent()){
renderText("Add successfully,there are %s records in the \'evaluation_part\' table.For convenience,please click the back button in the browser to go back previous page.",Part.count());
}
}
/**
* Fetch part entities from database;
*/
public static void fetchPart() {
//-------------------Solution 1-------------------
//[Pay attention]:Only use 'select new Part()'(constructor defined in the Part class) in the query that u can convert object to Part.
//Hibernate use reflection to automatically inject all the fields u need.
List<Part> parts1=JPA.em().createQuery("select new Part(name,collegeName,peopleNum) from Part").getResultList();
//For convenience, i output the detail in the console, focus on the change there.
Logger.info("The name of first record is :%s", parts1.get(0).name);
//-------------------Solution 2-------------------
//[Pay attention]:Here the returned type of result must be Object[],so that u can got every field of the record fetched from database;
List<Object[]> parts2=JPA.em().createNativeQuery("select name,collegeName,peopleNum from evaluation_part").getResultList();
Logger.info("The name of first record is :%s", parts2.get(0)[0]);
for(int i=0;i<parts2.size();i++){
//The difference between solution1 and solution2:the previous use constructor in the query and the later transform a record into Object[].
Logger.info("Name from parts1 is: %s", parts1.get(i).name);
Logger.info("Name from parts2 is: %s", parts2.get(i)[0]);
}
renderText("There are %s record in the \'evaluation_part\' table",parts2.size());
}
}
You can use the version of createNativeQuery(...) method that also accepts as argument the Class of the result instance(s):
http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#createNativeQuery(java.lang.String, java.lang.Class).
However make sure this actually works, as Play Framework doesn't implement all the features of JPA in it's implementation of the API.