JPQL Query - get child item in list from a parent - jpql

How would I create a JPQL query to get a Record if it exists in a Folder when I have the Folder :id and the Record :refId (named parameters). I would like to have the Record returned back from the query as there is other data that is part of the Record needed.
Here are the entities:
#Entity
public class Folder {
#Id
#Column(name = "id")
private Long id;
#OneToMany(targetEntity = Record.class, cascade = { CascadeType.ALL })
#JoinColumn(name = "RECORD_FOLDER_0")
#OrderBy("id ASC")
private List<Record> records;
...
}
#Entity
public class Record {
#Id
#Column(name = "id")
private Long id;
#Basic
#Column(name = "refId")
private Long refId;
#Basic
#Column(name = "data", length = 255)
private String data;
...
}
The regular SQL would look something like:
.createNativeQuery(String.format("SELECT * FROM record r WHERE r.refId=%d AND r.RECORD_FOLDER_0=%d", refId, folderId));
I am struggling on how to use a JPQL query to do this.

Since you don't have a reference to the parent Folder from Record, you'll have to start with the Folder and join the child Records. Something like this should do the trick:
TypedQuery<Record> q = em.createQuery(
"SELECT r FROM Folder f JOIN f.records r " +
"WHERE f.id = :folderId AND r.refId = :refId", Record.class);
q.setParameter("folderId", folderId);
q.setParameter("refId", refId);

Related

Cassandra UDT mapping issue

I am not able to map data which has UDT type.
The table is definition is the following.
CREATE TABLE IF NOT EXISTS members_data.Test(
priority int,
name text,
test_links list<frozen<TestLinks>>,
PRIMARY KEY(name)
);
The model is the following.
#JsonAutoDetect
#JsonSerialize
#Table(keyspace="members_data", caseSensitiveKeyspace=false, caseSensitiveTable=false, name="Test")
public class Test{
#Column(name="name", caseSensitive=false)
private String name;
#Column(name="priority", caseSensitive=false)
private int priority;
#Frozen
#Column(name="test_links", caseSensitive=false)
private List<TestLinks> test_links;
#JsonAutoDetect
#JsonSerialize
#UDT(keyspace = "members_data", name = "Testlinks")
public class TestLinks {
#Field(name = "test_link")
private String test_link;
#Field(name = "link_title")
private String link_title;
The mapper usage.
MappingManager manager = new MappingManager(sessionManager.getSession());
manager.udtCodec(TestLinks.class);
Mapper<Test> mapper = manager.mapper(Test.class);
Result<Test> result = mapper.map(testResultSet);
test = result.one(); //test object would be null here
the cassandra-driver-mapping is 3.1.0.
Mapper is not throwing any error and now even mapping data to model. Could someone tell me what is the problem?

JPQL, Where condition on Set with List of values

#Entity
public class Person{
private String firstName;
private String lastName;
#OneToMany(mappedBy="person" , fetch= FetchType.LAZY)
private Set<Certificate> certificates;
}
#Entity
public class Certificate{
private String courseName;
#ManyToOne
#JoinColumn(name="person_id")
private Person person;
}
Having an entity class(Person), which holds a collection(Set) of related entities(Certificate) mapped using #OneToMany as above.
Now I have to fetch all Persons having certificate.courseName IN ("OCPJP", "OCPWCD").
In Simple SQL, My query will be like this.
select distinct p.firstName, p.lastName from Person p join Certificate c on p.Id = c.person_Id
where c.courseName IN ("OCPJP", "OCPWCD")
How to get similar output in JPQL?
Such a query can be written as follows:
SELECT DISTINCT(c.person) FROM Certificate c WHERE c.courseName IN (:coursenames)
Complete example:
List<String> courseNames = Arrays.asList("OCPJP", "OCPWCD");
List<Person> result = em.createQuery(
"SELECT DISTINCT(c.person) " +
"FROM Certificate c " +
"WHERE c.courseName IN (:coursenames)", Person.class)
.setParameter("coursenames", courseNames)
.getResultList();

How to convert Cassandra UDT to Optional type

I have a User table and its corresponding POJO
#Table
public class User{
#Column(name = "id")
private String id;
// lots of fields
#Column(name = "address")
#Frozen
private Optional<Address> address;
// getters and setters
}
#UDT
public class Address {
#Field(name = "id")
private String id;
#Field(name = "country")
private String country;
#Field(name = "state")
private String state;
#Field(name = "district")
private String district;
#Field(name = "street")
private String street;
#Field(name = "city")
private String city;
#Field(name = "zip_code")
private String zipCode;
// getters and setters
}
I wanna convert UDT "address" to Optional.
Because I use "cassandra-driver-mapping:3.0.0-rc1" and "cassandra-driver-extras:3.0.0-rc1", there are lots of codec I can use them.
For example: OptionalCodec
I wanna register it to CodecRegistry and pass TypeCodec to OptionalCodec's constructor.
But TypeCodec is a abstract class, I can't initiate it.
Someone have any idea how to initiate OptionalCodec?
Thank you, #Olivier Michallat. Your solution is OK!
But I'm a little confused to set OptionalCodec to CodecRegistry.
You must initial a session at first.
Then pass session to MappingManager, get correct TypeCodec and register codecs.
It's a little weird that you must initial session at first, in order to get TypeCodec !?
Cluster cluster = Cluster.builder()
.addContactPoints("127.0.0.1")
.build();
Session session = cluster.connect(...);
cluster.getConfiguration()
.getCodecRegistry()
.register(new OptionalCodec(new MappingManager(session).udtCodec(Address.class)))
.register(...);
// use session to operate DB
The MappingManager has a method that will create the codec from the annotated class:
TypeCodec<Address> addressCodec = mappingManager.udtCodec(Address.class);
OptionalCodec<Address> optionalAddressCodec = new OptionalCodec(addressCodec);
codecRegistry.register(optionalAddressCodec);
Not really an answer but hope it helps. I couldn't make the Optional work with UDT in scala. However List and Array are working fine:
Here is a scala solution for driver version 4.x:
val reg = session.getContext.getCodecRegistry
val yourTypeUdt: UserDefinedType = session.getMetadata.getKeyspace(keyspace).flatMap(_.getUserDefinedType("YOUR_TYPE")).get
val yourTypeCodec: TypeCodec[UserDefinedType] = reg.codecFor(yourTypeUdt)
reg.asInstanceOf[MutableCodecRegistry].register(TypeCodecs.listOf(yourTypeCodec))
Don't forget to use java.util.* instead of your normal scala types.

JPQL: The state field path cannot be resolved to a valid type

I can't make this query work:
Query query = eManager.createQuery("select c FROM News c WHERE c.NEWSID = :id",News.class);
return (News)query.setParameter("id", newsId).getSingleResult();
and I got this exception:
Exception Description: Problem compiling [select c FROM News c WHERE c.NEWSID = :id].
[27, 35] The state field path 'c.NEWSID' cannot be resolved to a valid type.] with root cause
Local Exception Stack:
Exception [EclipseLink-0] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.JPQLException
Exception Description: Problem compiling [select c FROM News c WHERE c.NEWSID = :id].
Why does it happen?
:id and named parameter are identical
EDIT:
my entity class
#Entity
#Table(name="NEWS")
public class News implements Serializable{
#Id
#SequenceGenerator(name = "news_seq_gen", sequenceName = "news_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "news_seq_gen")
private int newsId;
private String newsTitle;
private String newsBrief;
private String newsContent;
private Date newsDate;
#Transient
private boolean selected=false;
//constructor and getters and setters
That happens because News entity does not have persistent attribute named NEWSID. Names of the persistent attributes are case sensitive in JPQL queries and those should be written with exactly same case as they appear in entity.
Because entity have persistent attribute named newsId, that should also be used in query instead of NEWSID:
select c FROM News c WHERE c.newsId = :id
entity have persistent attribute named newsId.but in query you have used NEWSID . try with this
select c FROM News c WHERE c.newsId = :id
My entity is:
#Entity
#Table(name = "TBL_PERSON_INFO")
public class Person implements Serializable {
#Id
#Column(name = "ID", nullable = false)
private Integer id;
#Column(name = "USER_ID", nullable = false)
private Integer user_id;
.
.
.
}
my query is (JPQL):
String queryName = "from Person p where p.user_id = :user_id";
so I use it like this:
javax.persistence.Query query = em.createQuery(queryName);
query.setParameter("user_id", userId);
try {
obj = query.getSingleResult();
}
catch (javax.persistence.NoResultException nre) {
logger.error("javax.persistence.NoResultException: " + nre.getMessage());
}
catch (javax.persistence.NonUniqueResultException nure) {
logger.error("javax.persistence.NonUniqueResultException: " + nure.getMessage());
}
catch (Exception e) {
e.printStackTrace();
}
if (obj == null) {
System.out.println("obj is null!");
return null;
}
Person person = (Person) obj;
It's work ;-)

How to execute ranged query in cassandra with astyanax and composite column

I am developing a blog using cassandra and astyanax. It is only an exercise of course.
I have modelled the CF_POST_INFO column family in this way:
private static class PostAttribute {
#Component(ordinal = 0)
UUID postId;
#Component(ordinal = 1)
String category;
#Component
String name;
public PostAttribute() {}
private PostAttribute(UUID postId, String category, String name) {
this.postId = postId;
this.category = category;
this.name = name;
}
public static PostAttribute of(UUID postId, String category, String name) {
return new PostAttribute(postId, category, name);
}
}
private static AnnotatedCompositeSerializer<PostAttribute> postSerializer = new AnnotatedCompositeSerializer<>(PostAttribute.class);
private static final ColumnFamily<String, PostAttribute> CF_POST_INFO =
ColumnFamily.newColumnFamily("post_info", StringSerializer.get(), postSerializer);
And a post is saved in this way:
MutationBatch m = keyspace().prepareMutationBatch();
ColumnListMutation<PostAttribute> clm = m.withRow(CF_POST_INFO, "posts")
.putColumn(PostAttribute.of(post.getId(), "author", "id"), post.getAuthor().getId().get())
.putColumn(PostAttribute.of(post.getId(), "author", "name"), post.getAuthor().getName())
.putColumn(PostAttribute.of(post.getId(), "meta", "title"), post.getTitle())
.putColumn(PostAttribute.of(post.getId(), "meta", "pubDate"), post.getPublishingDate().toDate());
for(String tag : post.getTags()) {
clm.putColumn(PostAttribute.of(post.getId(), "tags", tag), (String) null);
}
for(String category : post.getCategories()) {
clm.putColumn(PostAttribute.of(post.getId(), "categories", category), (String)null);
}
the idea is to have some row like bucket of some time (one row per month or year for example).
Now if I want to get the last 5 posts for example, how can I do a rage query for that? I can execute a rage query based on the post id (UUID) but I don't know the available post ids without doing another query to get them. What are the cassandra best practice here?
Any suggestion about the data model is welcome of course, I'm very newbie to cassandra.
If your use case works the way I think it works you could modify your PostAttribute so that the first component is a TimeUUID that way you can store it as time series data and you'd easily be able to pull the oldest 5 or newest 5 using the standard techniques. Anyway...here's a sample of what it would look like to me since you don't really need to make multiple columns if you're already using composites.
public class PostInfo {
#Component(ordinal = 0)
protected UUID timeUuid;
#Component(ordinal = 1)
protected UUID postId;
#Component(ordinal = 2)
protected String category;
#Component(ordinal = 3)
protected String name;
#Component(ordinal = 4)
protected UUID authorId;
#Component(ordinal = 5)
protected String authorName;
#Component(ordinal = 6)
protected String title;
#Component(ordinal = 7)
protected Date published;
public PostInfo() {}
private PostInfo(final UUID postId, final String category, final String name, final UUID authorId, final String authorName, final String title, final Date published) {
this.timeUuid = TimeUUIDUtils.getUniqueTimeUUIDinMillis();
this.postId = postId;
this.category = category;
this.name = name;
this.authorId = authorId;
this.authorName = authorName;
this.title = title;
this.published = published;
}
public static PostInfo of(final UUID postId, final String category, final String name, final UUID authorId, final String authorName, final String title, final Date published) {
return new PostInfo(postId, category, name, authorId, authorName, title, published);
}
}
private static AnnotatedCompositeSerializer<PostInfo> postInfoSerializer = new AnnotatedCompositeSerializer<>(PostInfo.class);
private static final ColumnFamily<String, PostInfo> CF_POSTS_TIMELINE =
ColumnFamily.newColumnFamily("post_info", StringSerializer.get(), postInfoSerializer);
You should save it like this:
MutationBatch m = keyspace().prepareMutationBatch();
ColumnListMutation<PostInfo> clm = m.withRow(CF_POSTS_TIMELINE, "all" /* or whatever makes sense for you such as year or month or whatever */)
.putColumn(PostInfo.of(post.getId(), post.getCategory(), post.getName(), post.getAuthor().getId(), post.getAuthor().getName(), post.getTitle(), post.getPublishedOn()), /* maybe just null bytes as column value */)
m.execute();
Then you could query like this:
OperationResult<ColumnList<PostInfo>> result = getKeyspace()
.prepareQuery(CF_POSTS_TIMELINE)
.getKey("all" /* or whatever makes sense like month, year, etc */)
.withColumnRange(new RangeBuilder()
.setLimit(5)
.setReversed(true)
.build())
.execute();
ColumnList<PostInfo> columns = result.getResult();
for (Column<PostInfo> column : columns) {
// do what you need here
}

Resources