The following JPA query doesn't compile:
#NamedQuery(name = "PSA.findBySourceSystem",
query = "SELECT p FROM PSA p WHERE p.sourceSystem.id = :sourceSystemId")
p.sourceSystem is the following enum:
public enum SourceSystem {
FIRST(3, "ABC"), SECOND(9, "DEF"), THIRD(17, "GHI");
private int id;
private String code;
...
}
and is mapped in PSA's base class:
public class PsaBase implements Serializable {
#Column(name = "sourceSystemId")
#Enumerated(EnumType.ORDINAL)
protected SourceSystem sourceSystem;
...
}
The query compiles and runs fine if I replace p.sourceSystem.id in the query with something more benign.
Thank you in advance for any help.
It shouldn't compile.
You have to resolve the required enum value manually before passing it as a query parameter:
#NamedQuery(name = "PSA.findBySourceSystem",
query = "SELECT p FROM PSA p WHERE p.sourceSystem = :sourceSystem")
.
public enum SourceSystem {
...
private static Map<Integer, SourceSystem> valuesById = new HashMap<Integer, SourceSystem>();
static {
for (SourceSystem s: values())
valuesById.put(s.id, s);
}
public static SourceSystem findById(int id) {
return valuesById.get(id);
}
}
.
em.createNamedQuery("PSA.findBySourceSystem")
.setParameter("sourceSystem", SourceSystem.findById(sourceSystemId));
EDIT:
Since sourceSystem is annotated as #Enumerated(EnumType.ORDINAL), it's stored in the database as the ordinal numbers of the corresponding enum values, therefore FIRST is stored as 0. JPA doesn't directly support using arbitrary field of the enum value to identify it in the database. If your database schema assumes so, you can do the following trick to decouple state of your object from the database schema:
public class PsaBase implements Serializable {
protected SourceSystem sourceSystem;
#Column(name = "sourceSystemId")
public Integer getSourceSystemId() {
return sourceSystem.getId();
}
public void setSourceSystemId(Integer id) {
this.sourceSystem = SourceSystem.findById(id);
}
... getter and setter of sourceSystem with #Transient ...
}
Related
I have a problem with the hibernate entity, and I would like to know if it is something I overlooked or if it is a bug in IntelliJ IDEA.
I have a Value object bank account:
class BankAccount
{
private String value;
public BankAccount(String value) {
// validation
this.value;
}
}
Which has defined it's own hibernate type:
public class BankAccountType extends AbstractSingleColumnStandardBasicType<BankAccount> {
public static final BankAccountType INSTANCE = new BankAccountType();
public static final String NAME = "bankAccount";
public BankAccountType() {
super(LongVarcharTypeDescriptor.INSTANCE, BankAccountTypeDescriptor.INSTANCE);
}
#Override
public String getName() {
return null;
}
}
And I have an entity:
#Entity
#TypeDefs({
#TypeDef(
name = BankAccountType.NAME,
typeClass = BankAccountType.class,
defaultForType = BankAccount.class
)
})
class User {
private UUID id;
//...
#Column
private BankAccount bankAccount;
//...
}
It works perfectly, but IDEA keeps telling me 'Basic attribute should not be BankAccount.'
Is there any way, how to get rid of this error without changing my entities? Is it a good idea to use value objects as a column in my entities?
Thanks a lot!
I am trying to access Tuple data structure I have stored in Cassandra with Mapper. But, I am unable to. I haven't found any example online.
This is the table and data I have created.
cqlsh:test> CREATE TABLE test.test_nested (id varchar PRIMARY KEY, address_mapping list<frozen<tuple<text,text>>>);
cqlsh:test> INSERT INTO test.test_nested (id, address_mapping) VALUES ('12345', [('Adress 1', 'pin1'), ('Adress 2', 'pin2')]);
cqlsh:test>
cqlsh:test> select * from test.test_nested;
id | address_mapping
-------+----------------------------------------------
12345 | [('Adress 1', 'pin1'), ('Adress 2', 'pin2')]
(1 rows)
My mapped class(using lombok for builder, getter, setter):
#Builder
#Table(keyspace = "test", name = "test_nested")
public class TestNested {
#PartitionKey
#Column(name = "id")
#Getter
#Setter
private String id;
#Column(name = "address_mapping")
#Frozen
#Getter
#Setter
private List<Object> address_mapping;
}
My Mapper class:
public class TestNestedStore {
private final Mapper<TestNested> mapper;
public TestNestedStore(Mapper<TestNested> mapper) {
this.mapper = mapper;
}
public void insert(TestNested userDropData) {
mapper.save(userDropData);
}
public void remove(String id) {
mapper.delete(id);
}
public TestNested findByUserId(String id) {
return mapper.get(id);
}
public ListenableFuture<TestNested> findByUserIdAsync(String id) {
return mapper.getAsync(id);
}
}
I am trying to access data in a test method as follows:
#Test
public void testConnection2(){
MappingManager manager = new MappingManager(scyllaDBConnector.getSession());
Mapper<TestNested> mapper = manager.mapper(TestNested.class);
TestNestedStore testNestedStore = new TestNestedStore(mapper);
ListenableFuture<TestNested> fut = testNestedStore.findByUserIdAsync("12345");
Futures.addCallback(fut, new FutureCallback<TestNested>() {
#Override
public void onSuccess(TestNested testNested) {
}
#Override
public void onFailure(Throwable throwable) {
System.out.println("Call failed");
}
});
}
Bit, I am unable to access the tuple. I get this error:
java.lang.IllegalArgumentException: Error while checking frozen types on field address_mapping of entity com.example.model.TestNested: expected List to be not frozen but was frozen
at com.datastax.driver.mapping.AnnotationChecks.validateAnnotations(AnnotationChecks.java:73)
at com.datastax.driver.mapping.AnnotationParser.parseEntity(AnnotationParser.java:81)
at com.datastax.driver.mapping.MappingManager.getMapper(MappingManager.java:148)
at com.datastax.driver.mapping.MappingManager.mapper(MappingManager.java:105)
I have also tried with private List<TupleValue> address_mapping;. But of no use!
How do I access Tuple values through object mapper of cassandra?
You define address_mapping as list<frozen<tuple<text,text>>>, that is, a list of frozen tuple values. To communicate this to the MappingManager, you can use the #FrozenValue attribute.
TestNested should look like:
#Builder
#Table(keyspace = "test", name = "test_nested")
public class TestNested {
...
#Column(name = "address_mapping")
#Frozen
#Getter
#Setter
#FrozenValue
private List<Object> address_mapping;
}
For defining the cassandra datatype of
map<text,frozen<tuple<text,text,int,text>>>
in java entity class mention the datatype as,
import com.datastax.driver.core.TupleValue;
#FrozenValue
private Map<String,TupleValue> event_driven;
I have the following native SQL query:
Select E.id AS ID, E.desc AS DESCRIPTION FROM TEMP E
And the dto class:
private int id;
private String description;
/* getter and setter */
How to get a list of dto class??
What I would probably do to maximize reuse would be to write my own ResultTransformer that you could instantiate prior to running your query and as a part of this implementation, you're required to provide it with the appropriate mapping information.
// construct the transformer and register mappings
MappedResultTransformertransformer = new MappedResultTransformer(DtoClass.class);
transformer.map( "ID", "id" );
transformer.map( "DESCRIPTION", "description" );
// apply the transformer
session.createQuery( ... ).setResultTransformer( transformer ).list();
Here's an example of how the transformer might look.
public class MappedResultTransformer extends BasicTransformerAdapter {
final Map<String, String> fieldMappings = new HashMap<>();
final Class<?> clazz;
public MappedResultTransformer(Class<?> clazz) {
this.clazz = clazz;
}
public void map(String alias, String property) {
fieldMappings.put( alias, property );
}
#Override
public Object transformTuple(Object[] tuple, String[] aliases) {
Object result = clazz.newInstance();
for ( int i = 0; i < aliases.length; ++i ) {
Object tupleValue = tuple[ i ];
String alias = aliases[ i ];
String propertyName = fieldMappings.get( alias );
if ( propertyName != null ) {
// use reflection to set the value of 'propertyName' on 'result'
}
}
return result;
}
}
The beauty here is that this class is completely reusable, it isn't tied to any specific class or query. You then could extend upon it and add support for nested properties, etc perhaps.
try this
Query query = getSession.createSQLQuery("...")
.addScalar("ID")
.addScalar("DESCRIPTION")
.setResultTransformer(Transformers.aliasToBean(dto.class));
List<dto> list = query.list();
dto class
#Entity
#Table(name="your database table")
public class DTO {
#Id
private int id
#Column(name="description_name_on_table)
private String description
..getter and setter
}
i'm on a final project now and making web app using hibernate.
my topic is recipe web site, and i have to show all the likers for a recipe
here's my pojo class for likes (not including the constructor and getter setter here to make it short code)
public class Likes implements java.io.Serializable {
private LikesId id;
private Member member;
private Resep resep;
private Integer likes;
public Likes() {
}
}
here's my pojo class for likes id (not including the getter setter here to make it short code)
public class LikesId implements java.io.Serializable {
private String idResep;
private String idMember;
public LikesId() {
}
public LikesId(String idResep, String idMember) {
this.idResep = idResep;
this.idMember = idMember;
}
}
now here is my method to show all likers for a recipe
public ArrayList<Likes> getAllLikes(String kode_resep)
{
this.session = NewHibernateUtil.getSessionFactory().openSession();
ArrayList<Likes> hasil = null;;
Transaction tx = session.beginTransaction();
Query q = session.createQuery("from Likes join LikesID on ()='"+kode_resep+"'");
hasil = (ArrayList<Likes>) q.list();
session.close();
return hasil;
}
the error when i run this method is:
Exception in thread "main"
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST
node: ( near line 1, column 35 [from model.Likes where
Likes.getId().getIdResep()='R001']
Try this:
Query q = session.createQuery("from Likes l where l.id.idResep = :code_resep");
q.setParameter("code_resep",kode_resep);
q.list();
I'm using SqlResultSetMapping and the Entity annotations (SqlResultSetMapping requires an Entity with an Id) to tell Hibernate how to populate instances of Foo with native query results data.
Non-persisted entity:
#SqlResultSetMapping(name = "fooMapping", entities = #EntityResult(entityClass = Foo.class))
#Entity
public class Foo {
#Id
public Long row_id;
public String name;
}
Native query:
String sql = "SELECT id AS row_id, friendlyName AS name FROM SomeTable";
Query q = JPA.em().createNativeQuery(sql, "fooMapping");
List<Foo> fooList = q.getResultList();
The problem is, a table called "Foo" gets created automatically for me (using Play! Framework in dev mode), but Foo is not a model and should not be persisted.
How do I instruct hibernate not to create this table?
Using #ConstructorResult will work great once it's available for your persistence layer. Until then, there is a Hibernate-specific approach using an org.hibernate.SQLQuery and an org.hibernate.transform.ResultTransformer that does not depend on #SqlResultSetMapping. Because a POJO is populated, Hibernate finds no #Entity to automatically turn into a table.
Non-persisted POJO:
public class Foo {
public Long row_id;
public String name;
}
ResultTransformer:
public static class FooResultTransformer implements ResultTransformer {
#Override
public List transformList(List list) { return list; }
#Override
public Object transformTuple(Object[] tuple, String[] aliases) {
List<String> aliasList = Arrays.asList(aliases);
Foo foo = new Foo();
foo.row_id = ((Number) getValue(tuple, aliasList, "row_id", 0L))
.longValue();
foo.name = (String) getValue(tuple, aliasList, "name", null);
return foo;
}
private static Object getValue(Object[] tuple, List<String> aliases,
String field, Object defaultValue)
{
// unchecked for berevity
if (tuple[aliases.indexOf(field)] == null) {
return defaultValue;
}
return tuple[aliases.indexOf(field)];
}
}
Native SQLQuery:
String sql = "SELECT id AS row_id, friendlyName AS name FROM SomeTable";
Session session = JPA.em().unwrap(Session.class);
SQLQuery q = session.createSQLQuery(sql);
q.setResultTransformer( new FooResultTransformer() );
List<Foo> fooList = q.list();
Unfortunately this isn't easy...
If you are using JPA 2.1 support for #ConstructorResult (seems there's only support in hibernate 4.3.0.Beta2 so you might not be using), you can use #ConstructorResult as follows:
#SqlResultSetMapping(name="fooMapping",
classes={
#ConstructorResult(targetClass=Foo.class, columns={
#ColumnResult(name="row_id", type=Integer.class),
#ColumnResult(name="name", type=String.class)
})
}
)
public class Foo {
public Long row_id;
public String name;
public Foo(Long rowId, String name) {
...
}
}