MyBatis - constructorArgs annotation and embedded objects - mybatis

Is it possible to map embedded object with annotation using MyBatis in one query i.e.:Foo class:
public class Foo //immutable
{
private final Integer fooId;
private final String fooAttr;
private final Bar bar;
public Foo(Integer fooId, String fooAttr, Bar bar) //constructor
}
Bar class:
public class Bar { //immutable
private final Integer barId;
private final String barAttr;
public Bar(Integer barId, String barAttr) //constructor
}
Table FooBar:
- INT foo_id
- CHAR foo_attr
- INT bar_id
- CHAR bar_attr
Query:
#ConstructorArgs({
#Arg(column="foo_id", javaType=Integer.class),
#Arg(column="foo_attr", javaType=String.class),
Bar ?
})
Select("select * from FooBar where foo_id=#{fooId}"
public Foo getFooBy(#Param("fooId") Integer fooId);
How to map Bar class as Foo constructor argument (without using subqueries) ?

I know, this is painful, but the following will work:
#ConstructorArgs({
#Arg(column="foo_id", javaType=Integer.class),
#Arg(column="foo_attr", javaType=String.class),
#Arg(column="bar_id", javaType=Integer.class),
#Arg(column="bar_attr", javaType=String.class)
})
The constructor should then look like this:
public Foo(Integer fooId, String fooAttr, Integer barId, String barAttr)
this.fooId = fooId;
this.fooAttr = fooAttr;
this.bar = new Bar(barId, barAttr);
}
You could even leave Bar empty, if the ID is not set:
public Foo(Integer fooId, String fooAttr, Integer barId, String barAttr)
this.fooId = fooId;
this.fooAttr = fooAttr;
if (barId != null) {
this.bar = new Bar(barId, barAttr);
}
}

Related

Is it possible to write custom attributes or annotation in a vala class, like C# or Java?

as the question implies how can one reduce boilerplate code or add custom functionality like for example in Java
#Setter(AccessLevel.PROTECTED) private String name;
or Jackson-annotation #JsonProperty
how could one implement or emulate this functionality?
thanks
No, you can't create custom attributes right now.
For properties you can use next syntax:
public string name { get; protected set; default = "42"; }
GObject type system also support Json Serialization/Deserialization for properties.
Here small example(valac file --pkg json-glib-1.0):
public enum MyEnum {
FOO, BAR, FOOBAR
}
public class MyObject : Object {
public string str { get; set; }
public MyEnum en { get; set; }
public int num { get; set; }
public MyObject (string str, MyEnum en, int num) {
this.str = str;
this.num = num;
this.en = en;
}
}
public static int main (string[] args) {
MyObject obj = new MyObject ("my string", MyEnum.FOOBAR, 10);
Json.Node root = Json.gobject_serialize (obj);
// To string: (see gobject_to_data)
Json.Generator generator = new Json.Generator ();
generator.set_root (root);
string data = generator.to_data (null);
// Output:
// ``{"str":"my string","en":2,"num":10}``
print (data);
print ("\n");
return 0;
}
You can find more on ValaDoc.

Sorting strings based on the integer value they represent in hibernate search

I have an entity with a string field in it.
Sometimes this string field stores actual words, so sorting based on lexicographical order makes sense. However, there is a use case where this field stores string values like "0%", "10%", "100%", "20%".
The default string sorting generates the following sequence: 0% 10% 100% 20%.
The ideal sequence after sorting would be 0%, 10%, 20%, 100%(determined by the percentage they represent).
It is guaranteed that there is no mixed data, i.e, you won't have "10%" and "word" appear in the same sequence that needs to be sorted.
My question is if there is a way to achieve the string sorting based on the numerical value they represent?
Ideally, you should alter your model to have two fields: one of integer type where you'll hold the percentage, and one of string type where you will hold the string value:
#Entity
#Indexed
public class MyEntity {
#Field
#SortableField
private int percentage;
#Field
#SortableField
private String notPercentage;
// ... other properties, getters and setters ...
}
Then when sorting you'll just sort on both fields. Since you are certain they are mutually exclusive, it's not a problem to sort on both:
QueryBuilder queryBuilder = ...;
FullTextQuery ftQuery = ...;
ftQuery.sort(queryBuilder.sort().byField("percentage").andByField("notPercentage").createSort());
Alternatively, if you really want to keep a single String property in your ORM model (to each his own...), you can use a custom bridge to apply padding to your values, so that they are sorted correctly:
#Entity
#Indexed
public class MyEntity {
#Field(bridge = #FieldBridge(impl = PaddingIfPercentageBridge.class))
#SortableField
private String percentageOrNot;
// ... other properties, getters and setters ...
}
public class PaddingIfPercentageBridge implements TwoWayStringBridge {
private static final Pattern PERCENTAGE = Pattern.compile("[0-9]+%");
#Override
public String objectToString(Object object) {
if ( object == null ) {
return null;
}
String string = (String) object;
if ( !PERCENTAGE.matcher( string ).matches() ) {
return string;
}
StringBuilder paddedPercentage = new StringBuilder();
for ( int padIndex = string.length(); padIndex < 4; padIndex++ ) {
paddedPercentage.append( '0' );
}
return paddedPercentage.append( string ).toString();
}
#Override
public Object stringToObject(String stringValue) {
// Ideally you should remove the padding here...
return stringValue;
}
}
Then you can sort on a single field:
QueryBuilder queryBuilder = ...;
FullTextQuery ftQuery = ...;
ftQuery.sort(queryBuilder.sort().byField("percentageOrNot").createSort());

Disadvantages of interface objected programming

class Person{
private String name;
private int age;
private String gender;
//......
}
class Student extends Person{
private String id;
private String schoolBelongTo;
//......
}
public void showInfoOf(Person person){
System.out.println(person.getName());
//......
}
When using function "showInfoOf" ,if an object of Peron is used as the param,OK.However,if it is the type Student,I cannot get access to the field id and schoolBelongTo.
So I am confused ,how to ?
Actually, I want to know is this one of its(Interface oriented programming's or Supper class oriented programming's) disadvantages???
Two possible solutions:
You can programatically check the type in showInfoOf (Person), and use a cast to access & print the desired fields; or,
You can define a method on Person which will print/provide the desired info -- and either replace showPersonInfo() with that entirely, or call it into it. This is the more OO way.
Example:
abstract class Person {
private String name;
private int age;
private String gender;
public void printInfo() {
System.out.println( name);
}
}
class Student extends Person{
private String id;
private String schoolBelongTo;
#Override
public void printInfo() {
super.printInfo();
System.out.println( id);
System.out.println( schoolBelongTo);
}
}
public void showInfoOf (Person person){
person.printInfo();
}
In this example, all functionality has moved to Person.printInfo() and there is no real functionality remaining in showInfoOf (Person).
However in the real-world, you'd probably want move versatility in a Person.provideInfo() function -- perhaps returning a LinkedHashMap of fields & values (since unlabelled values on their own, are not great design).
The showInfoOf (Person) function could then handle formatting & printing the values to the specific requirement, leaving the Person.provideInfo() function general & multi-purpose.
in showInfoOf() you would have to check that person is of type Student, then cast it as a Student to get id or schoolBelongsTo

EclipseLink native query result into POJO - Missing descriptor for [Class]

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)

JPA composite PK is not working with 'finder' in play framework

I have a model called 'UserRoleHolder' like below.
#Entity
public class UserRoleHolder extends Model implements RoleHolder {
private static final long serialVersionUID = 1L;
#EmbeddedId
public UserRoleHolderPK userRoleHolderPK;
public List<UserPermission> permissions;
public List<UserRole> roles;
....
I made a composite PK called UserRoleHolderPK and it contains two foreign keys like below.
#Embeddable
public class UserRoleHolderPK {
#Basic
public Long userId;
#Basic
public Long projectId;
public UserRoleHolderPK(Long userId, Long projectId) {
this.userId = userId;
this.projectId = projectId;
}
public boolean equals(Object object) {
if (object instanceof UserRoleHolderPK) {
UserRoleHolderPK userRoleHolderPK = (UserRoleHolderPK) object;
return userId == userRoleHolderPK.userId && projectId == userRoleHolderPK.projectId;
} else {
return false;
}
}
public int hashCode() {
return (int) (userId + projectId);
}
}
userId and projectId are from other Models. (User.java and Project.java)
Then, in 'UserRoleHolder' class, I made a method called 'findRolesById' like below.
public static List<? extends Role> findRolesById(Long userId, Long projectId) {
return find
.where()
.eq("userRoleHolderPK", new UserRoleHolderPK(userId, projectId))
.findUnique().roles;
}
However, when I tried to run a test code like below, I encountered serious errors.
#Test
public void findRolesById() {
// Given
// When
#SuppressWarnings("unchecked")
List<UserRole> list = (List<UserRole>) UserRoleHolder.findRolesById(1l, 1l);
// Then
assertThat(list.get(0).name).isEqualTo("manager");
}
Errors are like,
'Syntax error in SQL statement "SELECT T0.USER_ID C0, T0.PROJECT_ID C1 FROM USER_ROLE_HOLDER T0 WHERE T0.NULL[*] = ? "; expected "identifier"; SQL statement: select t0.user_id c0, t0.project_id c1 from user_role_holder t0 where t0.null = ? [42001-158]
Bind values:[null]
Query was:
select t0.user_id c0, t0.project_id c1 from user_role_holder t0 where t0.null = ?
I think I missed some serious and basic stuff when I used JPA. Please, let me know what is the problem.
I think your problem is that you are trying to compare the Embeddedid object and not its fields, I don't think that the program will be smart enough as to know how to convert an user object comparison (the equals) to sql, so you might want to try something like this:
public static List<? extends Role> findRolesById(Long userId, Long projectId) {
return find
.where()
.eq("userRoleHolderPK.userId", userId)
.eq("userRoleHolderPK.projectId", projectId)
.findUnique().roles;
}