I try to have such code:
Query<Card> query = ofy().load().type(Card.class);
UserData creator = ofy().load().type(UserData.class).id(creatorId).now();
if (creator != null && UserType.USER.equals(creator.getUserType())) {
query.filter("creator", creator);
}
if (orderColumnName != null) {
query.order((ascending ? "" : "-") + orderColumnName);
}
query.offset(startRow).limit(limit);
return query.list();
But it doesn't filter.
Also this filter:
UserData creator = ofy().load().type(UserData.class).id(creatorId).now();
Query<Card> query = ofy().load().type(Card.class).filter("creator", creator);
Any idea why?
EDITED
My class Card.java
#Entity
public class Card implements PersistableObject {
#Id
Long id;
#Index
Date createDate;
...
#Index
Ref<UserData> creator;
...
public UserData getCreator() {
if (creator != null) {
return creator.get();
}
return null;
}
public void setCreator(UserData creator) {
this.creator = Ref.create(creator);
}
}
My class UserData.java
#Entity
public class UserData implements PersistableObject {
#Id
Long id;
Ref<EaistoAccount> eaistoAccount;
UserType userType;
public EaistoAccount getEaistoAccount() {
if (eaistoAccount == null) {
return null;
}
return eaistoAccount.get();
}
public void setEaistoAccount(EaistoAccount aistoAccount) {
this.eaistoAccount = Ref.create(aistoAccount);
}
}
It doesn't work means that I expect to get filtered entities to corresponding UserData but it doesn't filter when I split query in a few parts also it filters when I use query in one line.
I have found a solution:
Why aren't my queries working properly? All of Objectify's
intermediate command objects are immutable. This will not work:
Query q = ofy().load().type(Foo.class); q.filter("bar", bar);
List foos = q.list(); The filter command did nothing because you
did not reassign q. You need this:
q = q.filter("bar", bar); Alternatively, chain the whole sequence in a
single statement. Read more here.
https://code.google.com/p/objectify-appengine/wiki/FrequentlyAskedQuestions
Related
I'm using EclipseLink to run some Native SQL. I need to return the data into a POJO. I followed the instructions at EclipseLink Docs, but I receive the error Missing descriptor for [Class]
The query columns have been named to match the member variables of the POJO. Do I need to do some additional mapping?
POJO:
public class AnnouncementRecipientsFlattenedDTO {
private BigDecimal announcementId;
private String recipientAddress;
private String type;
public AnnouncementRecipientsFlattenedDTO() {
super();
}
public AnnouncementRecipientsFlattenedDTO(BigDecimal announcementId, String recipientAddress, String type) {
super();
this.announcementId = announcementId;
this.recipientAddress = recipientAddress;
this.type = type;
}
... Getters/Setters
Entity Manager call:
public List<AnnouncementRecipientsFlattenedDTO> getNormalizedRecipientsForAnnouncement(int announcementId) {
Query query = em.createNamedQuery(AnnouncementDeliveryLog.FIND_NORMALIZED_RECIPIENTS_FOR_ANNOUNCEMENT, AnnouncementRecipientsFlattenedDTO.class);
query.setParameter(1, announcementId);
return query.getResultList();
}
I found out you can put the results of a Native Query execution into a List of Arrays that hold Objects. Then one can iterate over the list and Array elements and build the desired Entity objects.
List<Object[]> rawResultList;
Query query =
em.createNamedQuery(AnnouncementDeliveryLog.FIND_NORMALIZED_RECIPIENTS_FOR_ANNOUNCEMENT);
rawResultList = query.getResultList();
for (Object[] resultElement : rawResultList) {
AnnouncementDeliveryLog adl = new AnnouncementDeliveryLog(getAnnouncementById(announcementId), (String)resultElement[1], (String)resultElement[2], "TO_SEND");
persistAnnouncementDeliveryLog(adl);
}
You can only use native SQL queries with a class if the class is mapped. You need to define the AnnouncementRecipientsFlattenedDTO class as an #Entity.
Otherwise just create the native query with only the SQL and get an array of the data back and construct your DTO yourself using the data.
Old question but may be following solution will help someone else.
Suppose you want to return a list of columns, data type and data length for a given table in Oracle. I have written below a native sample query for this:
private static final String TABLE_COLUMNS = "select utc.COLUMN_NAME, utc.DATA_TYPE, utc.DATA_LENGTH "
+ "from user_tab_columns utc "
+ "where utc.table_name = ? "
+ "order by utc.column_name asc";
Now the requirement is to construct a list of POJO from the result of above query.
Define TableColumn entity class as below:
#Entity
public class TableColumn implements Serializable {
#Id
#Column(name = "COLUMN_NAME")
private String columnName;
#Column(name = "DATA_TYPE")
private String dataType;
#Column(name = "DATA_LENGTH")
private int dataLength;
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public int getDataLength() {
return dataLength;
}
public void setDataLength(int dataLength) {
this.dataLength = dataLength;
}
public TableColumn(String columnName, String dataType, int dataLength) {
this.columnName = columnName;
this.dataType = dataType;
this.dataLength = dataLength;
}
public TableColumn(String columnName) {
this.columnName = columnName;
}
public TableColumn() {
}
#Override
public int hashCode() {
int hash = 0;
hash += (columnName != null ? columnName.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
if (!(object instanceof TableColumn)) {
return false;
}
TableColumn other = (TableColumn) object;
if ((this.columnName == null && other.columnName != null) || (this.columnName != null && !this.columnName.equals(other.columnName))) {
return false;
}
return true;
}
#Override
public String toString() {
return getColumnName();
}
}
Now we are ready to construct a list of POJO. Use the sample code below to construct get your result as List of POJOs.
public List<TableColumn> findTableColumns(String table) {
List<TableColumn> listTables = new ArrayList<>();
EntityManager em = emf.createEntityManager();
Query q = em.createNativeQuery(TABLE_COLUMNS, TableColumn.class).setParameter(1, table);
listTables = q.getResultList();
em.close();
return listTables;
}
Also, don't forget to add in your POJO class in persistence.xml! It can be easy to overlook if you are used to your IDE managing that file for you.
Had the same kind of problem where I wanted to return a List of POJOs, and really just POJOs (call it DTO if you want) and not #Entity annotated Objects.
class PojoExample {
String name;
#Enumerated(EnumType.STRING)
SomeEnum type;
public PojoExample(String name, SomeEnum type) {
this.name = name;
this.type = type;
}
}
With the following Query:
String query = "SELECT b.name, a.newtype as type FROM tablea a, tableb b where a.tableb_id = b_id";
Query query = getEntityManager().createNativeQuery(query, "PojoExample");
#SuppressWarnings("unchecked")
List<PojoExample> data = query.getResultList();
Creates the PojoExample from the database without the need for an Entity annotation on PojoExample. You can find the method call in the Oracle Docs here.
edit:
As it turns out you have to use #SqlResultSetMapping for this to work, otherwise your query.getResultList() returns a List of Object.
#SqlResultSetMapping(name = "PojoExample",
classes = #ConstructorResult(columns = {
#ColumnResult(name = "name", type = String.class),
#ColumnResult(name = "type", type = String.class)
},
targetClass = PojoExample.class)
)
Just put this anywhere under your #Entity annotation (so in this example either in tablea or tableb because PojoExample has no #Entity annotation)
I am currently developing a data warehouse with spring boot, hibernate and querydsl.
Nearly everything is working fine, but I got trouble doing a search request for one of my entity called group. The errors, are not really helpful:
My request is simple /group/advancedSearch?page=0&size=10&sort=name,asc&search=groupCode:dfa,name:dfa,
The errors raise in my service when I do call the repository method.
antlr.NoViableAltException: unexpected token: group
[...]
java.lang.NullPointerException: null
To make this more understandable my code is below. I have the same method for most of my entities and there it is working fine. Because I had no clue where the unexpected token group might come from, I had a look at the generated class QGroup, there I found this peace of code public static final QGroup group = new QGroup("group1");. The name group1 made me wonder, but I'm not sure if it has anything to do with the errors. In all other classes the string was always the name of the class with initial letters small.
I thought the entity group might be duplicated, so querydsl would create group and group1, but that's not the case. So any ideas where the errors might come from and how to prevent / fix them?
The entity:
#Entity
#Table(name = "[Group]")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Group_ID")
private long groupId;
#ManyToOne()
#JoinColumn(name = "Dimension_ID")
private Dimension dimension;
#Column(name = "Dimension_ID", updatable = false, insertable = false)
private Long dimensionId;
#Column(name = "GroupCode")
private String groupCode;
#Column(name = "Name")
private String name;
[...]
}
The function of the controller where the errors raise:
#RequestMapping(value = GROUP_URL + "/advancedSearch", method = RequestMethod.GET)
#ResponseBody
public PagedResources<Group> advancedSearch(
#RequestParam(value = "search", required = false) String search,
Pageable pageable, #RequestParam MultiValueMap<String, String> parameters,
PersistentEntityResourceAssembler persistentEntityResourceAssembler
) {
SimpleGrantedAuthority[] allowedRoles = {SYSADMIN};
GeneralPredicateBuilder<Group> builder = new GeneralPredicateBuilder<>(Group.class);
Predicate predicate = predicateService.getPredicateFromParameters(parameters, Group.class);
Page<Group> results = service.advancedSearch(
this.buildAdvancedSearch(search, predicate, builder), pageable, allowedRoles);
return super.toPagedResource(results, persistentEntityResourceAssembler);
}
public Predicate buildAdvancedSearch(String search, Predicate predicate, GeneralPredicateBuilder<T> builder) {
if (search != null) {
Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),");
Matcher matcher = pattern.matcher(search + ",");
while (matcher.find()) {
builder.with(matcher.group(1), matcher.group(2), matcher.group(3));
}
BooleanExpression expression = builder.build();
if (predicate != null) {
predicate = expression.and(predicate);
} else {
predicate = expression;
}
}
return predicate;
}
The PredicateService:
#Service
public class PredicateService {
#Autowired
private final QuerydslPredicateBuilder querydslPredicateBuilder;
#Autowired
private final QuerydslBindingsFactory querydslBindingsFactory;
public PredicateService(QuerydslPredicateBuilder querydslPredicateBuilder, QuerydslBindingsFactory querydslBindingsFactory) {
this.querydslPredicateBuilder = querydslPredicateBuilder;
this.querydslBindingsFactory = querydslBindingsFactory;
}
public <T> Predicate getPredicateFromParameters(final MultiValueMap<String, String> parameters, Class<T> tClass) {
TypeInformation<T> typeInformation = ClassTypeInformation.from(tClass);
return querydslPredicateBuilder.getPredicate(typeInformation, parameters, querydslBindingsFactory.createBindingsFor(typeInformation));
}
}
The service method:
public Page<Group> advancedSearch(Predicate predicate, Pageable pageable, SimpleGrantedAuthority[] roles){
if (SecurityUtils.userHasAnyRole(roles)) {
return this.repository.findAll(predicate, pageable); // <-- here the errors raise
} else throw new ForbiddenException(FORBIDDEN);
}
The repository:
#RepositoryRestResource(collectionResourceRel = GROUP_URL, path = GROUP_URL)
#CrossOrigin(exposedHeaders = "Access-Control-Allow-Origin")
public interface GroupRepository extends PagingAndSortingRepository<Group, Long>, JpaSpecificationExecutor<Group>, QuerydslPredicateExecutor<Group> {
}
The generated class QGroup by querydsl:
#Generated("com.querydsl.codegen.EntitySerializer")
public class QGroup extends EntityPathBase<Group> {
private static final long serialVersionUID = 384278695L;
private static final PathInits INITS = PathInits.DIRECT2;
public static final QGroup group = new QGroup("group1"); // <-- this is confusing
[...]
Update:
I finally found the generated query:
select group1
from Group group1
where ?1 = ?1 and lower(group.groupCode) like ?2 escape '!'
I think here is the problem. Form a SQL developer view, group.groupCode should be group1.groupCode. Anyone knows how to fix this?
Update 2 [2020-02-14]:
The GeneralPredicateBuilder:
public class GeneralPredicateBuilder<T> {
private List<SearchCriteria> params;
private final Class<T> type;
public GeneralPredicateBuilder(Class<T> type) {
this.params = new ArrayList<>();
this.type = type;
}
public GeneralPredicateBuilder<T> with(String key, String operation, Object value) {
params.add(new SearchCriteria(key, operation, value));
return this;
}
public BooleanExpression build() {
if (params.size() == 0) {
return null;
}
List<BooleanExpression> predicates = params.stream().map(param -> {
GeneralPredicate<T> predicate = new GeneralPredicate<T>(param, type);
BooleanExpression tmp = predicate.getPredicate();
return tmp;
}).filter(Objects::nonNull).collect(Collectors.toList());
BooleanExpression result = Expressions.asBoolean(true).isTrue();
for (BooleanExpression predicate : predicates) {
result = result.and(predicate);
}
return result;
}
public List<Predicate> buildPredicate(){
if (params.size() == 0) {
return null;
}
return params.stream().map(param -> {
GeneralPredicate<T> predicate = new GeneralPredicate<>(param, type);
return predicate.getPredicate();
}).filter(Objects::isNull).collect(Collectors.toList());
}
}
I still don't understand why the, by querydsl, generated classname of Group is group1, but in combination with my GenericPredicateBuilder and the GenericPredicate this leads to the inconsistent sql, as shown in the question. But I was finally able to fix this, unfortunately in a really dirty way. For completeness here is my GeneralPredicate:
public class GeneralPredicate<T> {
private SearchCriteria searchCriteria;
private final Class<T> type;
private final String variable;
public GeneralPredicate(SearchCriteria param, Class<T> type) {
searchCriteria = param;
this.type = type;
if(type.getSimpleName().equals("Group")){
this.variable = "group1";
} else {
this.variable = type.getSimpleName().replaceFirst("" + type.getSimpleName().charAt(0), "" + type.getSimpleName().charAt(0)).toLowerCase();
}
}
public BooleanExpression getPredicate() {
PathBuilder<T> entityPath = new PathBuilder<T>(type, variable);
if (isNumeric(searchCriteria.getValue().toString())) {
NumberPath<Integer> path = entityPath.getNumber(searchCriteria.getKey(), Integer.class);
int value = Integer.parseInt(searchCriteria.getValue().toString());
switch (searchCriteria.getOperation()) {
case ":":
return path.eq(value);
case ">":
return path.goe(value);
case "<":
return path.loe(value);
}
} else {
StringPath path = entityPath.getString(searchCriteria.getKey());
switch (searchCriteria.getOperation()) {
case ":":
return path.containsIgnoreCase(searchCriteria.getValue().toString());
case "<":
return path.startsWith(searchCriteria.getValue().toString());
case ">":
return path.endsWith(searchCriteria.getValue().toString());
}
}
return null;
}
}
You find the dirty fix within the constructor. I really hate it, but it is working and the generated sql is okay.
Maybe I use the generic in a wrong way. I am open for advices.
basically I got following entity (extended by Lombok)
#Getter
#Setter
#Entity("FOO")
public class Foo{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
private long id;
#ManyToOne
#JoinColumn(name = "FK_FEE", nullable = false)
private Fee fee;
#Column(name = "CCCS")
#Convert(converter = StringListConverter.class)
private List<String> cccs;
}
And the StringListConverter:
#Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {
#Override
public String convertToDatabaseColumn(final List<String> list) {
String returnValue = null;
if (list != null) {
final List<String> trimmedList = new ArrayList<>();
for (final String strg : list) {
if (strg != null && !strg.isEmpty()) {
trimmedList.add(strg.trim());
}
}
returnValue = String.join(",", trimmedList);
}
return returnValue;
}
#Override
public List<String> convertToEntityAttribute(final String joined) {
List<String> returnValue = null;
if (joined != null) {
returnValue = new ArrayList<>();
final String[] splitted = joined.split(",");
for (final String strg : splitted) {
if (strg != null && !strg.isEmpty()) {
returnValue.add(strg.trim());
}
}
}
return returnValue;
}
}
And now I want to fetch a List of Foo where Fee.Id= 123 and Foo.cccs contains a specific string value.
#Repository
public interface FooRepository extends CrudRepository<Foo, Long> {
List<Foo> findByFeeIdAndCccsIn(Long feeId, String ccc);
}
But this does not work. Is the only way to solve this by writing an own query?
#Repository
public interface FooRepository extends CrudRepository<Foo, Long> {
#Query( "Select foo FROM Foo foo WHERE foo.fee.id = :feeId AND foo.cccs LIKE CONCAT(CONCAT('%', :cccs) ,'%')")
List<Foo> findByFeeIdAndCccsIn(#Param("feeId") Long feeId,#Param("cccs") String ccc);
}
Currently it seems the only solution is to use #Query and write a JPQL or native SQL query to get the data.
String SELECT_BY_FEE_AND_CCC = "Select foo FROM Foo foo "
+ " WHERE foo.fee.id = :feeId AND foo.cccs LIKE CONCAT(CONCAT('%', :ccc) ,'%')";
and attached with the #Query annotation like:
#Query(SELECT_BY_FEE_AND_CCC)
List<Foo> findByFeeIdAndCccsContains(#Param("feeId") Long feeId, #Param("ccc") String ccc);
NOTE/EDIT:
Sometimes you have to join the foreign table to have access on it's properties.
String SELECT_BY_FEE_AND_CCC = "Select foo FROM Foo foo "
+ " JOIN foo.fee fee "
+ " WHERE fee.id = :feeId AND foo.cccs LIKE CONCAT(CONCAT('%', :ccc) ,'%')";
And now I want to fetch a List of Foo where Fee.Id= 123 and Foo.cccs
contains a specific string value.
You have specific requirements. According to Spring Data JPA doc what you are trying to do is not supported.
So you should use #Query for your requirements.
I have a query. But it does not work. I tested it on Oracle SQL Developer, It worked. So I wrote it in namedquery.
The query is below that works fine on Oracle SQL Developer
SELECT q.* FROM TAKE_EXAM_QUESTION q
INNER JOIN TAKE_EXAM e
on q.tk_exam_id = e.tk_exam_id
where e.user_id= :userId;
And I typed above query in Entity class
#NamedQuery(name = "TakeExamQuestionEntity.question", query = "SELECT qs FROM TakeExamQuestionEntity qs INNER JOIN TakeExamEntity ex on qs.tk_exam_id = ex.tk_exam_id where ex.user_id= :userId "),
But it is not working, I do not know why now working please suggest me.
If you generate entity from table, any fields of table will be remove _ and first character after _ will be upper case. Thus, you should write NamedQuery as below example.
#NamedQuery(name = "TakeExamQuestionEntity.question",
query = "SELECT qs FROM TakeExamQuestionEntity qs
INNER JOIN TakeExamEntity ex on qs.tkExamId = ex.tkExamId
where ex.userId= :userId ")
If it not work, you should check you entity they are separated to 2 class (entity Primary Key and entity class) or not.
#Embeddable
public class EmployeePK implements Serializable {
private String name;
private long id;
public EmployeePK() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int hashCode() {
return (int) name.hashCode() + id;
}
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof EmployeePK)) return false;
if (obj == null) return false;
EmployeePK pk = (EmployeePK) obj;
return pk.id == id && pk.name.equals(name);
}
}
#Entity
public class Employee implements Serializable {
EmployeePK primaryKey;
public Employee() {
}
#EmbeddedId
public EmployeePK getPrimaryKey() {
return primaryKey;
}
public void setPrimaryKey(EmployeePK pk) {
primaryKey = pk;
}
...
}
If your entity has generated as 2 class as above example.
You should define
SELECT e FROM Employee e
where e.primaryKey.name=:name
the primaryKey will be an object of EmployeePK that has annotation Embeddable.
If you want to use native query you should use #NamedNativeQuery instead of #NamedQuery.
See also: NamedNativeQuery
init-datasample.yml
Book(a): &a
title: Play in Action
price: 30.00
Book(b): &b
title: Alice in Wonderland
price: 12.00
Person(b):
name: Bob Joe
ratings:
? *a: 8
? *b: 8
Java Class Definition for Person
#Entity
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Person extends Model
{
public String name;
// choice and importance score
#ElementCollection(targetClass=Integer.class)
#MapKeyClass(Book.class)
public Map<Book, Integer> ratings;
}
The Fixture.loadModels("init-datasample.yml") does not load the initial ratings defined in the above. I tried not to put &a, &b, it didn't work either. Could someone please help?
Thank you.
Ok, got the work around. Would like to share here. And if anyone can take a look and think it is worth commit to the git source, please help to do so.
The problem is the embedded is not support at this moment. So the resolveDependencies in the Fixtures class does not resolve the reference in the map defined.
My work aorund is to extend Fixtures class, and hide the loadModels methods. And
inside the code block of
Fixtures.loadmodels(String name, Map<String, Object> idCache) {
... ...
if (f.getType().isAssignableFrom(Map.class)) {
/* new below */
ElementCollection ec = f.getAnnotation(ElementCollection.class);
MapKeyClass mkc = f.getAnnotation(MapKeyClass.class);
if (ec != null && mkc != null) {
Map mapper = (Map) objects.get(key).get(f.getName());
if (mapper == null) continue;
Class targetClass = ec.targetClass();
Class mapKeyClass = mkc.value();
Map<Object, Object> fieldValue = new HashMap();
for (Object k : mapper.keySet()) {
Object mapKey = retrieveObject(mapKeyClass, k, idCache);
Object targetValue = retrieveObject(targetClass, mapper.get(k), idCache);
fieldValue.put(mapKey, targetValue);
}
f.set(model, fieldValue);
} else {
f.set(model, objects.get(key).get(f.getName()));
}
}
... ...
}
/* new method below */
private static Object retrieveObject(Class targetClass, Object alias, Map<String, Object> idCache)
{
if (targetClass.isAssignableFrom(alias.getClass())) {
return alias;
}
Object targetValue = idCache.get(targetClass.getCanonicalName() + "-"+ alias);
if (targetValue != null) {
targetValue = Model.Manager.factoryFor(targetClass).findById(targetValue);
}else {
try {
targetValue = targetClass.getConstructor(String.class).newInstance(alias.toString());
} catch(Exception ex) {
ex.printStackTrace();
return null;
}
}
return targetValue;
}
In case, someone wanted to refer to an already existing entity (whitch was loaded in an earlier #OnApplicationStart for example) you can refer them by the ids:
Fixture:
User(user01):
email: user01#g.com
userGroup.id: ${models.UserGroup.getGroupForYml(models.UserGroup.GROUP_SALES)}
UserGroup Model:
#Entity
public class UserGroup extends Model {
public static final String GROUP_ADMIN = "admin";
public static final String GROUP_CUSTOMER = "customer";
public static final String GROUP_SALES = "sales";
#OneToMany(mappedBy = "userGroup")
public List<User> userList;
#Column(name="groupId")
public String group;
/**
* Used in the yml fixtures
* #param group - the group id
*/
public static Long getGroupForYml(String group) {
UserGroup ug = UserGroup.find("byGroup", group).first();
return ug.id;
}
}
User Model:
#Entity
#Table(name = "users")
public class User extends BaseModel {
publis String email;
#ManyToOne
public UserGroup userGroup;
}