I have the following problem. I have three classes, A, B and C. A contains a OneToMany relationed list of B:s. B contains a ManyToOne relation to C. C contains a field called "name" and B also contains a field called "name". What I'd like to accomplish is to have the items in A's list sorted primarily by C's name and secondarily by B's name - the problem is that I do not know how to do this. Is it even possible?
I'm using EclipseLink as my JPA provider.
class A {
#OneToMany
#OrderBy("b.c.name, b.name") <---- this is the problem
List<B> b;
}
class B {
#ManyToOne
C c;
String name;
}
class C {
String name;
}
EDIT
Yes, I've tried different variations, for example #OrderBy("c.name") doesn't work, I just get an error message telling me that the entity class b does not contain a field called "c.name".
It's NOT possible. #OrderBy only accepts direct property / field names, not nested properties. Which makes sense, really, because "c" table - depending on your fetching strategy may not even be part of a select issued to retrieve your "b"s.
ChssPly76 is right.
What you could do is to create a named query like this one:
SELECT b
FROM B b
WHERE b.a = :mya
ORDER BY b.c.name
Have you tried #OrderBy("c.name", "name") ?
You shouldn't use "b." because it's implied that the #OrderBy will be done on columns of the instances of B on the b array.
Have you tried:
#OrderBy("c.name ASC", "name ASC")
?
It is not possible in javax.persistence.OrderBy (as say ChssPly76 ), but when I was using Hibernate I construct new column in PLAIN SQL with Formula() annotation and then OrderBy over it:
class A {
#OneToMany
#OrderBy("orderCol") <---- reference to virtual column
List<B> b;
}
class B {
#ManyToOne
C c;
String name;
#org.hibernate.annotations.Formula(
"( select C_table.name as orderCol from C_table where C_table.id = id )"
) <------------------------------------------ join with plain SQL statment
String orderCol;
}
May be EclipseLink has same possibilities?
Related
I have a question about Mappers in Mybatis. I have two classes like this:
public class A{
private String A1;
private String A2;
private List<B> listB;
//getters and setters
.
.
.
public static class B {
private String B1;
private String B2;
//getters and setters
.
.
.
}
}
Then I have a mapper class like this:
#Mapper
public interface ABMapper{
#Select("select b1,b2 from b where b.a1 = #{a1}")
public List<B> getBs(#Param("a1") String a1);
#Select ("select a1,a2 from a limit 100")
#Results({
#Result(property="a1", value = "a1"),
#Result(property="a2", value = "a2"),
#Result(property="listB", column="a1", many = #Many(select = "getBs"))
})
public List<A> getAs();
}
This works fine, but I know that when getAs() executes, getBs runs as many times as items have (limit 100 is an example).
I wonder that if exists a way to run a query like select a.a1,a.a2,b.b1,b.b2 from a a inner join b b on a.a1 = b.a1 and then Mybatis (and Java) could group elements in List<A> and the attribute B is not empty.
Perhaps, it necessary to use hash and equals in class A and B, but I don't know.
Thanks for your answers.
Mybatis can do that but only if you use xml mapping. The limitation of annotations in java makes it impossible to map associations with join:
You will notice that join mapping is not supported via the Annotations API. This is due to the limitation in Java Annotations that does not allow for circular references.
In this case the mapping might look like:
<resultMap id="bMap" type="B">
<id property="b1" column="b1"/>
<result property="b2" column="b2"/>
</resultMap>
<resultMap id="aMap" type="A">
<id property="a1" column="a1"/>
<result property="a2" column="a2"/>
<collection property="listB" javaType="B" resultMap="bMap" columnPrefix="b_"/>
</resultMap>
<select id='getAs' resultMap='aMap'>
SELECT a.*, b.id B_id, b.b1 B_b1, b.b2 B_b2
FROM (
select *
from a
LIMIT 100
) AS a
LEFT JOIN AS b on a.a1 = b.a1
</select>
Some important notes:
both A and B should have some identifying field(s) configured with id element. The value in this field would be used to identify object and do what you call grouping. For table a this seems to be a1 (as you used it as a join field) and I used it in the example.
autoMapping="true" in resultMap might be useful if there are many fields to be mapped
you need to use left/right join to handle those records from table a that do not have anything in b.
in order to LIMIT work correctly with join you need to do it on the select that gets records from a and not on the join result otherwise you may get less than 100 records in the result if more than 100 records from b are joined.
it depends on the use case but usually if you use LIMIT you need to specify some order, otherwise records will be returned in unpredictable order.
In the older versions of mybatis there was a bug that required that column prefixes in the query should be in upper case (may be this is fixed now, I'm not sure).
My entity class is something like this:
#Entity
public class Book {
...
#ElementCollection
private List<String> authors;
...
}
Now I want query books by inputting an author's name, e.g. Tom, but ignore the name's case so tom/tOm/... should also match. I know that I can achieve using a collection member declaration:
select b from Book b, in(b.authors) a where lower(a) = 'tom'
However, in our application framework all JPQL statements are generated from a fixed template:
select b from Book b where WHERE_CLAUSE
I'm only allowed to provide the where clause. Anybody know how to get the same results as above with this template?
Finally I found a cheating way:
select b from Book b where (b.id in (select bb from Book bb, in(bb.authors) a where lower(a) = 'tom'))
I have two tables which I need to be joined but there in no relation specified in the entity. Can I write something like
select uc.id, uc.name, mpn.name from UCR uc, MpnMapping mpn
I'm getting an error called Exception in thread "main" org.hibernate.hql.ast.QuerySyntaxException: unexpected token
Yes you can use new operator and a DTO class to set the values..
Query
select new com.example.UCRMNP(uc.id, uc.name, mpn.name) from UCR uc, MpnMapping mpn;
DTO class
First can create a DTO class and specify all the column variables, like this
class UCRMNP{
int id;
String name;
String name1;
public UCRMNP( int id, String name, String name1){
this.id=id;
this.name=name;
this.name1=name1;
}
}
Now you can set the retrieved data to that specific DTO class, You dont want to
annotate it with anything, just specify the constructor and execute
the Query using new operator.
Yes, you can do it by specifing the join in the where clause. It must be something like this:
select a, b from A a, B b where a.joinColumn = b.joinColumn
You can see more info in the below links:
Joining two unrelated tables in hibernate
http://www.codewrecks.com/blog/index.php/2009/09/04/theta-join-in-hql-join-with-unrelated-entities/
I have two entities, A and B. When I merge A, I want B to be merged as well - so I never do operations on B, just always operations on A. The B object will be accessible via the A object, but not vice versa (if possible). A and B share the same primary key field, that is, the primary key field of B is a foreign key to the primary key of A. However, I've finally got it to the point where the INSERT queries are in the correct order, but because it's not binding the ID in query parameters, it's throwing an InsertNullViolation as the primary key is not set. Does anyone know how to get EclipseLink to bind the primary key for the joined objects like B?
public class A {
...
#Id
#Column(name = "A_ID")
#SequenceGenerator(...)
#GeneratedValue(...)
public Long getA_ID();
#OneToOne(mappedBy = "a", targetEntity = B.class)
public B getB();
...
}
public class B {
...
#Id
public Long getA_ID();
#MapsId
#OneToOne(targetEntity = A.class)
#JoinColumn(name="A_ID")
public A getA();
...
}
The above setup does not set the primary key on B so throws an InsertNullViolation trying to insert into B.
public class A {
...
#Id
#Column(name = "A_ID")
#SequenceGenerator(...)
#GeneratedValue(...)
public Long getA_ID();
#OneToOne(mappedBy = "a", targetEntity = B.class)
public B getB();
...
}
public class B {
...
#Id
#OneToOne(targetEntity = A.class)
#JoinColumn(name="A_ID")
public A getA();
...
}
This setup also has the same issue. The exception is:
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("SOME_USER"."B"."A_ID")
Error Code: 1400
Call: INSERT INTO B (SOME_FIELD1, SOME_FIELD2, ..., A_ID) VALUES (?, ?, ..., ?)
bind => [null, SOME_VALUE, ..., null]
Query: InsertObjectQuery(packagename.B#ObjectId)
That last null on the bind line needs to be filled in by EclipseLink with the actual ID that it previously retrieved from the sequence when it inserted object A.
What I'm doing for merging boils down to essentially this:
public void merge(A objectA, B objectB) {
objectA.setB(objectB);
entitymanager.merge(objectA);
}
The issue seems to be that you are not setting the A on your B.
You must maintain bi-directional relationships, there is no magic that does this for you.
When you create your A and assign the B you also must assign the A to the B, otherwise it is null, and will insert null. Thus, you should be doing this instead:
public void merge(A objectA, B objectB) {
A.setB(objectB); //so when you merge A it knows about the B objects
B.setA(objectA); //so B knows to look at the A object for it's key
entitymanager.merge(objectA);
}
Further, in your first example, you also have a duplicate A_ID field in B, so you must also set this value when setting the A of B. Ensure you have persisted A first, otherwise its Id will be null, you could avoid this by removing the A_ID and putting the #Id on the OneToOne as you have done in your second example. You could also put insert/updateable=false in the A_ID and true in the OneToOne, then the foreign key value will come from the OneToOne.
You get the multiple writable mappings error because both the A_ID and the OneToOne map the same column, you need to mark one of them insert/updateable=false.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Primary_Keys_through_OneToOne_and_ManyToOne_Relationships
and,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Object_corruption.2C_one_side_of_the_relationship_is_not_updated_after_updating_the_other_side
Here's the documentation on how to do it with Hibernate. Since it's standard JPA, you should be able to do the same thing with EclipseLink.
Either you replace the A_ID in B with a mapped OneToOne association to A (and use mappedBy on the other side), or you keep the A_ID, but also add a OneToOne association to A and annotated it with #MapsId (and you also use mappedBy on the other side).
I got this error because I been doing em.persist(parent), even when I been doing parent.setChildren(child). So I had to add child.setParent(parent). But it's relative to the entity you are persisting because you also can do em.persist(child). For brevity, try to set the relation in both entities.
An extra comment, when I did what I'm telling in the previous lines then I got an "ORA-02291" error, having an inconsistence with my foreign key. It was due that I had a TRIGGER to set up the PK of my table and I was also using #SequenceGenerator in my entity. To solve this, I had to validate in my TRIGGER that only the PK can be set up to MYSEQUENCE.nexval if :new.PK_TABLE is null.
Hope this could help someone with the same trouble because I was stuck with this problem since yesterday and it was too much wasting of time.
I have 2 entities: EntityA and EntityB.
They are unrelated, and I cannot put them in a Inheritance tree for some restrictions out of the scope of this question.
But I need to get in the same JPQL or HQL query a mixed List containing all the instances of both entities. Is this possible with JPA or even Hibernate directly?
I need somethign like this:
FROM EntityA WHERE fieldA=1
UNION
FROM EntityB WHERE fieldB="aa"
Any hint?
Well, I finally figured it out.
It is enought to make the entities implement a common interface (it is not even needed to declare this interface on Hibernate).
Then, a query like this can be done:
FROM my.package.CommonInterface obj
WHERE obj IN (FROM EntityA WHERE fieldA=1) OR
obj IN (FROM EntityB WHERE fieldB='a')
This way, you retrieve a List<CommonInterface>.
Problem solved.
Best thing is to performs two queries.
But if you must:
You can create a POJO to retrieve them:
class EntityAandEntityB {
EntityA a;
EntityB b;
long idA;
long idB;
int fieldA;
String fieldB;
public EntityAandEntityB(long idA, long IdB, int fieldA, String fieldB) {
this.a = new EntityA(idA, fieldA);
this.b = new EntityB(idB, fieldB);
}
}
Then your query would be:
select new package.EntityAandEntityB(a.idA, a.fieldA, b.idB, b.fieldB) from (
(select idA, fieldA from EntityA) a
UNION
(select idB, fieldB from EntityB) b)
This is dirty and you probably must to look carefully the syntax.
Regards.