Persisting a Collection class with ORMLite in android - java

I have two classes setup like the following. I am confused as to when I need to annotate something as an foreign collection and when I do not. This may also sound silly, but nowhere in the ORMLite documentation does it say whether or not a non-foreign collection is allowed. What if I have a List of ints which get autoboxed into Integers? can I just persist this using a standard #DatabaseField above the Collection? A foreign collection, according to ORMLite, must also have back reference for it to work (a reference to the parent, given a one to many realtionship). For the example below, I am assuming you should annotate myBList as a foreign collection as well as making myA a foreign object, but how could you handle myStringList?
I Have seen sample code here but it doesn't answer my questions: http://ormlite.com/docs/examples
public class A {
private Set<B> myBList = new HashSet<B>();
private List<String> myStringList = new ArrayList<String>();
private long id;
public A(){}
public Set<B> getMyBList() {
return myBList;
}
public void setMyBList(Set<B> myBList) {
this.myBList = myBList;
}
public List<String> getMyStringList() {
return myStringList;
}
public void setMyStringList(List<String> myStringList) {
this.myStringList = myStringList;
}
public void setId(long id){
this.id = id;
}
public long getId(){
return id;
}
}
public class B {
private int myInt;
private String myString;
private A myA;
private long id;
public B(){}
public A getMyA(){
return myA;
}
public A setMyA(A a){
myA = a;
}
public int getMyInt() {
return myInt;
}
public void setMyInt(int myInt) {
this.myInt = myInt;
}
public String getMyString() {
return myString;
}
public void setMyString(String myString) {
this.myString = myString;
}
public void setId(long id){
this.id = id;
}
public long getId(){
return id;
}
}

#Robert is correct. When hibernate persists a collection (or even an array), it does so with hidden extra tables with foreign ids -- in other words hidden foreign collections. ORMLite tries to adhere to the KISS principle and so has you define the foreign collections "by hand" instead.
I've added more details about storing collections.
http://ormlite.com/docs/foreign-collection
This means that you cannot persist an Integer type because there is no foreign-id. Also, your code can define a foreign collection Collection<Order> or ForeignCollection<Order>. Either one will be set with a ForeignCollection. ORMLite does not support lists or other collection types.

If you want to save a Collection (such as an ArrayList) of objects to ORMLite the easiest way is this:
#DatabaseField(dataType = DataType.SERIALIZABLE)
private SerializedList<MyObject> myObjects;
and to get my list of objects:
public List<MyObject> getMyObjects() {
return myObjects;
}

Related

How to map foreign key attribute to record in Java with JDBI

Say my DB looks like this, presenting using POJO:
class A {
long id; // auto-increment primary key
String aAttribute;
}
class B {
long id; // auto-increment primary key
long aId; // foreign key of class A
String bAttribute;
}
How could I naturally map the DB records to class B using JDBI so the class B could contain the actual object of A instead of a foreign key to A:
class B {
long id; // auto-increment primary key
A a; // actual object of class A
String bAttribute;
}
One approach (there are others, also) is to use the JDBI #Nested annotation with a bean mapper. The annotation:
"...creates a mapper for the nested bean."
Place the annotation on the relevant setter (or getter). So, in your case that would be like this:
import org.jdbi.v3.core.mapper.Nested;
import org.jdbi.v3.core.mapper.reflect.ColumnName;
public class B {
private long id; // auto-increment primary key
private A a; // actual object of class A
private String bAttribute;
#ColumnName("b_id")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public A getA() {
return a;
}
#Nested
public void setA(A a) {
this.a = a;
}
#ColumnName("b_attribute")
public String getBAttribute() {
return bAttribute;
}
public void setBAttribute(String bAttribute) {
this.bAttribute = bAttribute;
}
}
I have also added #ColumnName annotations to disambiguate the otherwise identical column names between the two objects (and, presumably, the tables).
Here is class A:
package com.northcoder.jdbidemo;
import org.jdbi.v3.core.mapper.reflect.ColumnName;
public class A {
private long id; // auto-increment primary key
private String aAttribute;
#ColumnName("a_id")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#ColumnName("a_attribute")
public String getAAttribute() {
return aAttribute;
}
public void setAAttribute(String aAttribute) {
this.aAttribute = aAttribute;
}
}
A query therefore needs to use column aliases to match these annotations:
String sql = """
select b.id as b_id, b.bAttribute as b_attribute, a.id as a_id, a.aAttribute as a_attribute
from your_db.a as a
inner join your_db.b as b
on a.id = b.a_id;
""";
jdbi.useHandle(handle -> {
List<B> bees = handle
.select(sql)
.mapToBean(B.class)
.list();
});
Each instance of class B in the resulting list will contain an instance of A (assuming the data exists in the database).

Is it correct to have static factory method to get a new instance with one field updated?

I think the title is self-descriptive but I will give an example to elaborate on my question. I have a DTO class with few fields (a CarDataTransferObj class in my example). In another class (let's call it class A) I need to create a new instance of that object few times, but with only one field updated (length field in my example). Given DTO must be immutable in class A. As there is "many" fields in the class CarDataTransferObj, I thought about following approach (to avoid repeating code in class A):
#Builder
public class CarDataTransferObj {
private Integer id;
private String color;
private String manufacturer;
private String model;
private String uniqueIdNr;
private Integer nrOfDoors;
private EngineType engineType;
private Integer length;
private Integer safetyLevel;
public static CarDataTransferObj newInstanceWithUpdatedLength(final CarDataTransferObj car, final Integer newLength) {
return CarDataTransferObj.builder()
.id(car.getId())
.color(car.getColor())
.manufacturer(car.getManufacturer())
.model(car.getModel())
.uniqueIdNr(car.getUniqueIdNr())
.nrOfDoors(car.getNrOfDoors())
.engineType(car.getEngineType())
.length(newLength)
.safetyLevel(car.getSafetyLevel())
.build();
}
}
For me it smells like a little anti-pattern usage of static factory methods. I am not sure whether it's acceptable or not, hence the question.
Is using static factory method in the presented way an anti-pattern, and should be avoided ?
In my searching, I didn't come across anyone calling this1 an anti-pattern.
However, it is clear that if you try to do this using a classic builder that is not specifically implemented to support this mode of operation .... it won't work. For instance, the example CarBuilderImpl in the Wikipedia article on the Builder design pattern puts the state into an eagerly created Car instance. The build() method simply returns that object. If you tried to reuse that builder in the way that you propose, you would end up modifying a Car that has already been built.
There is another problem you would need to worry about. In we modified the Wikipedia CarBuilder example to add actual wheels (rather than a number of wheels) to the Car being built, we have to worry about creating cars that share the same wheels.
You could address these things in a builder implementation, but it is unclear whether the benefits out-weigh the costs.
If you then transfer this thinking to doing this using a factory method, you come to a slightly different conclusion.
If you are doing this as a "one-off", that's probably OK. You have a specific need, the code is clunky ... but so is the problem.
If you needed to do this for lots of different parameters, or combinations of parameters, this is not going to scale.
If the objects that are created are mutable, then this approach is could be problematic in a multi-threaded environment depending on how you control access to the objects you are using as templates.
1 - There are no clear measurable criteria for whether something is an anti-pattern or not. It is a matter of opinion. Admittedly, for many anti-patterns, there will be wide-scale agreement on that opinion.
It seems a little inefficient to construct an entirely new instance via a builder every time you want to make a new copy with a small modification. More significantly, it sounds like the places where you need the class to be immutable are isolated to places like class A. Why not try something like this:
public interface ICarDataTransferObject {
public Integer GetId();
public String GetColor();
public String GetManufacturer();
public String GetModel();
public String GetUUID();
public Integer GetDoorCount();
public EngineType GetEngineType();
public Integer GetLength();
public Integer GetSafteyLevel();
}
public class CarDataTransferObject Implements ICarDataTransferObject {
private Integer _id;
private String _color;
private String _manufacturer;
private String _model;
private String _uniqueIdNr;
private Integer _nrOfDoors;
private EngineType _engineType;
private Integer _length;
private Integer _safetyLevel;
public Integer GetId() { return _id; }
public void SetId(Integer id) { _id = id; }
public String GetColor() { return _color; }
public void SetColor(String color) { _color = color; }
public String GetManufacturer() { return _manufacturer; }
public void SetManufacturer(String manufacturer) { _manufacturer = manufacturer; }
public String GetModel() { return _model; }
public void SetModel(String model) { _model = model; }
public String GetUUID() { return _uniqueIdNr; }
public void SetUUID(String uuid) { _uniqueIdNr = uuid; }
public Integer GetDoorCount() { return _nrOfDoors; }
public void SetDoorCount(Integer count) { _nrOfDoors = count; }
public EngineType GetEngineType() { return _engineType; }
public void SetEngineType(EngineType et) { _engineType = et; }
public Integer GetLength() { return _length; }
public void SetLength(Integer length) { _length = length; }
public Integer GetSafteyLevel() { return _safetyLevel; }
public void SetSafteyLevel(Integer level) { _safteyLevel = level; }
public CarDataTransferObject() {}
public CarDataTransferObject(ICarDataTransferObject other) { ... }
public ReadOnlyCarDataTransferObject AsReadOnly() {
return ReadOnlyCarDataTransferObject (this);
}
}
}
public class ReadOnlyCarDataTransferObject Implements ICarDataTransferObject {
private ICarDataTransferObject _dto = null;
public Integer GetId() { return _dto.GetId(); }
public String GetColor() { return _dto.GetColor(); }
public String GetManufacturer() { return _dto.GetManufacturer(); }
public String GetModel() { return _dto.GetModel(); }
public String GetUUID() { return _dto.GetUUID(); }
public Integer GetDoorCount() { return _dto.GetDoorCount(); }
public EngineType GetEngineType() { return _dto.GetEngineType(); }
public Integer GetLength() { return _dto.GetLength(); }
public Integer GetSafteyLevel() { return _dto.GetSafteyLevel; }
public ReadOnlyCarDataTransferObject (ICarDataTransferObject other) {
_dto = other;
}
}
Now when you want class A to have a copy no one can modify, just use the copy constructor and only expose a ReadOnly version of that copy.
public class A {
ICarDataTransferObject _dto;
ReadOnlyCarDataTransferObject _readOnlyDTO;
public ICarDataTransferObject GetDTO() { return _readOnlyDTO; }
public A(ICarDataTransferObject dto) {
_dto = new CarDataTransferObject(dto);
_readOnlyDTO = new ReadOnlyCarDataTransferObject(_dto);
}
}
You commonly see this approach in .NET applications.
While it is debatable whether your static method is an anti-pattern or not, it surely won't scale for combinations of different attributes. Nonetheless, even if it's not an anti-pattern, I think there is a better way to accomplish what you need.
There's a variant of the traditional builder pattern that, instead of creating a new empty builder, accepts an already built object and creates an already initialized builder. Once you create the builder this way, you simply change the length attribute in the builder. Finally, build the object. In plain code (no Lombok, sorry) it could be like this:
public class CarDataTransferObj {
private Integer id;
private String color;
// other attributes omitted for brevity
private Integer length;
// Private constructor for builder
private CarDataTransferObj(Builder builder) {
this.id = builder.id;
this.color = builder.color;
this.length = builder.length;
}
// Traditional factory method to create and return builder
public static Builder builder() {
return new Builder();
}
// Factory method to create and return builder initialized from an instance
public static Builder builder(CarDataTransferObj car) {
Builder builder = builder();
builder.id = car.id;
builder.color = car.color;
builder.length = car.length;
return builder;
}
// getters
public static class Builder {
private Integer id;
private String color;
private Integer length;
private Builder() { }
public Builder withId(Integer id) { this.id = id; return this; }
public Builder withColor(String color) { this.color = color; return this; }
public Builder withLength(Integer length) { this.length = length; return this; }
public CarDataTransferObj build() {
return new CarDataTransferObj(this);
}
}
}
Now with all this infrastructure in place, you can do what you want as easy as:
CarDataTransferObj originalCar = ... // get the original car from somewhere
CarDataTransferObj newCar = CarDataTransferObj.builder(originalCar)
.withLength(newLength)
.build();
This approach has the advantage that it scales well (it can be used to change any combination of parameters). Maybe all this builder's code seems boilerplate, but I use an IntelliJ plugin to create the builder with two keystrokes (including the variant factory method that accepts a built instance to create an initialized builder).
I'm still new to java but..
I guess making a copy method which takes the CarDataTransferObj object variables and sets their values to another CarDataTransferObj object variables and changing the the length using it's setter method would be better idea
Example:
public class CarDataTransferObj {
private Integer id;
private String color;
private String manufacturer;
private String model;
private String uniqueIdNr;
private Integer nrOfDoors;
private EngineType engineType;
private Integer length;
private Integer safetyLevel;
public void Copy(CarDataTransferObj copy) { //Could add another parameter here to be the new length
copy.setId(id);
copy.set(color);
copy.setManufacturer(manufacturer);
copy.setModel(model);
copy.setUniqueIdNr(uniqueIdNr));
copy.setNrOfDoors(nrOfDoors));
copy.setEngineType(engineType));
copy.setLength(length);
copy.setSafetyLevel(safetyLevel));
}
}
public class SomeOtherClass {
CarDataTransferObj car1 = new CarDataTransferObj(); //Using this way made you able to use the constructor for a more useful thing
//You set the variables you want for car1 here
CarDataTransferObj car2 = new CarDataTransferObj();
car1.Copy(car2)
car2.setLength(newLength) //Set the new length here
}

Observe removed objects in lists

We are currently using JaVers 3.0.0. Suppose we have the following two entities A and B. And A keeps track of some Bs in a list.
#Entity
#TypeName("A")
public class A {
#Id
private int id;
private List<B> items;
public A() {
items = new ArrayList<>();
}
public A(int id) {
this();
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<B> getItems() {
return items;
}
public void setItems(List<B> items) {
this.items = items;
}
}
And here is our rather simple class B.
#Entity
#TypeName("B")
public class B {
#Id
private int id;
public B() {
}
public B(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
Lets commit three changes to an object of type A:
Commit object with empty list
Commit object with two added Bs
Commit object with one B removed
After that I want to observe changes on B.
Javers javers = JaversBuilder
.javers()
.build();
A a = new A(1);
javers.commit("foo#example.com", a);
a.getItems().add(new B(1));
a.getItems().add(new B(2));
javers.commit("foo#example.com", a);
a.getItems().remove(0);
javers.commit("foo#example.com", a);
List<Change> changes = javers.findChanges(
QueryBuilder.byClass(B.class)
.build());
String changelog = javers.processChangeList(changes, new SimpleTextChangeLog());
System.out.println(changelog);
The output says zero changes. I was expecting to see a removed object since B is an entity and has an Id. What am I missing?
Edit
Thanks for answering in the comments so far. Maybe I wasn't detailed enough. Sorry about that.
What I am trying to query is all the changes on A and all the changes on B. I only commit A, but maybe that is the problem? Should I track A and B?
javers.compare() and javers.commit() don't work in the same way.
compare() simply compares two object graphs, without any context.
That's why you could expect ObjectRemoved on the change list when comparing graphs.
But commit() is for auditing changes.
Since you've mapped both classes as Entities, they are independent. B objects can't be marked as deleted just because they are no longer referenced by A objects.
The only way to mark them as deleted (and to have ObjectRemoved change) is by calling commitShallowDelete()

save mongo entity to the different collections

I've been using Spring Data for saving entities to the mongo DB and my code at the moment looks like this:
I have a repo class:
public interface LogRepo extends MongoRepository<Log, String> {
}
and I have an Entity Log which looks like this:
#Document(
collection = "logs"
)
public class Log {
#Id
private String id;
private String jsonMessage;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getJsonMessage() {
return jsonMessage;
}
public void setJsonMessage(String jsonMessage) {
this.jsonMessage = jsonMessage;
}
}
and this work well for me, however this approach works only for the case if I want to save Log entities to "logs" collection. However it would be very nice for me to be able to save Log entity to different collections depending on the context. I mean it would be nice to define collection name in the runtime. Is it possible somehow?
Thanks, cheers
Try to use inheritance and define appropriate collection names in such way. May give you possibility to save in different collections but you will be still not able to specify dynamically collection names and resp. their amount at runtime.
#Document(
collection = "logs"
)
public class Log {
#Id
private String id;
private String jsonMessage;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getJsonMessage() {
return jsonMessage;
}
public void setJsonMessage(String jsonMessage) {
this.jsonMessage = jsonMessage;
}
}
#Document(
collection = "log_child"
)
public class LogChild extends Log{}
With the MongoOperations save method you can choose which class to use and
based on the class it will choose the appropriate collection.
#Document(collection = "collection_#{T(com.github.your_project.DBUtils).getCollectionName()}")
public Class Collection
You can change the name in real time using a static getter
#UtilityClass
public class DBUtils {
private String collectionName;
public String getCollectionName() {
return collectionName;
}
public void setCollectionName(String collectionName) {
DBUtils.collectionName = collectionName;
}
}

Hibernate annotation for base java class

I would like to put into db a class that have java.awt.geom.Point2D field. Is it possible?
Here is my code.
#Entity
#Table(name = "my_class_table")
public class MyClass {
private String aliasId;
private Point2D field;
public Point2D getField() {
return field;
}
public void setFieldPoint2D field) {
this.field = field;
}
public String getAliasId() {
return aliasId;
}
public void setAliasId(String aliasId) {
this.aliasId = aliasId;
}
}
And the reason of the exception which is thrown:
Could not determine type for: java.awt.geom.Point2D, at table: my_class_table, for columns: [org.hibernate.mapping.Column(field)]
Of course, the reason is quite obvious. My question is: how should I annotate the class to be able to use a field of Point2D class? Is it possible at all?
The simplest way is to use a java.awt.Point that extends Point2D and is a Serializable class. This way hibernate will automatically map it with SerializableType and you don't need to do anything more. The point object will be saved in its serialized form in a blob database table column.
You have also the option to define a custom hibernate type for the Point2D class. Here is a link of how to define a custom hibernate type.
You can't add annotations to existing classes.
But you can define a CompositeUserType to tell Hibernate how to map a Point2D.
Thanks guys for response. Unfortunatelly java.awt.Point class uses Integer, so it is useless in my case. The easiest way to solve it would be to use Point2D.Double which implements Serializable (but definition of UserType or CompositeUserType is more convenient if you don't want to change class definition). So, the simple solution:
#Entity
#Table(name = "my_class_table")
public class MyClass {
private String aliasId;
private Point2D.Double field;
public Point2D.Double getField() {
return field;
}
public void setField(Point2D.Double field) {
this.field = field;
}
public String getAliasId() {
return aliasId;
}
public void setAliasId(String aliasId) {
this.aliasId = aliasId;
}
}
But my final goal was to create a class with ordered list of points. If anybody is interested here is an example of the class representing line:
#Entity
public class Line {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idDb", unique = true, nullable = false)
private int id;
#ElementCollection
#CollectionTable(name="points_table", joinColumns = #JoinColumn(name="idDb"))
#IndexColumn(name = "idx")
#Column(name="point_val")
private List<Point2D.Double> points = new ArrayList<Point2D.Double>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<Point2D.Double> getPoints() {
return points;
}
public void setPoints(List<Point2D.Double> points) {
this.points = points;
}
}

Categories