Ebean manyToMany not saved in transaction - playframework-2.2

I'm using play-framework 2.2.1 with Ebean. I have a manyToMany relantionship between Student and SchoolClass.
Inside a transaction, when a schoolClass is created, added to Student and saved, the respective bridge table is not filled.
#Entity
public class Student extends Model {
...
#ManyToMany(cascade = CascadeType.ALL)
public List<SchoolClass> schoolClasses = new ArrayList<SchoolClass>();
}
#Entity
public class SchoolClass extends Model {
...
#ManyToMany(mappedBy = "schoolClasses")
public List<Student> students = new ArrayList<Student>();
}
Code:
try {
Ebean.beginTransaction();
...
Student student = new Student();
student.schoolClasses.add(schoolClass);
student.save();
Ebean.commitTransaction();
} finally {
Ebean.endTransaction();
}
Any ideas?

I have once experienced a similar problem, could you try it like the following:
List<SchoolClass> classes = new ArrayList<SchoolClass>();
classes.add(schoolClass);
student.schoolClasses.addAll(classes);

Related

Querydsl nested query generated an error sql

I wrote a simple nested query
#Autowired
#PersistenceContext
EntityManager entityManager;
#Autowired
Configuration configuration;
private JPASQLQuery<?> queryFactory;
#PostConstruct
public void init() {
queryFactory = new JPASQLQuery<>(entityManager, configuration);
}
public void student() {
QStudent student = new QStudent("student");
QStudent nested = new QStudent("nested");
List<?> fetch = queryFactory.select(student.id)
.from(SQLExpressions.select(nested.id)
.from(nested), student)
.fetch();
}
The entity structure is like this
#Entity
#Table(name = "u_student")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
The generated sql is like this
select student.id
from u_student (select nested.id
from u_student nested) as student
Outside sql added a tableName up, causing sql syntax error
Am I wrong? How should such a nested query be written?
The first time you send a question is not very useful, thank you.
Found that using PathBuilder can solve this problem
The complete code is:
PathBuilder<Tuple> studentAlias = new PathBuilder<>(Tuple.class,"student");
QStudent student = new QStudent("student");
QStudent nested = new QStudent("nested");
List<?> fetch = queryFactory.select(student)
.from(SQLExpressions.select(nested.id)
.from(nested), studentAlias)
.fetch();
Generated sql:
select student.id as id1_90_0_
from (select nested.id from u_student nested) as student
Do you have any better plans?

eclipselink/jpa inheritance problems

I am using spring boot (2.0.0) with eclipse link to persist data (over 500 entity classes) to a postgres db (6.5). Thats works very well. For receiving the data over REST I build an other spring boot application. Here I have some inheriance problem with JPA (sorry for my drawing):
Class C and class D (abstract) inherit from class B. Class A have a reference (attribute1) to class B. This attribute is an instance of entity class E, which inherit from abstract class D. I am using inheritance strategy table per class. Every class using the annotation Entity with the table name. In the database, table from class A have a correct foreign key to table from class E, but if I want to read the data the attribute1 is null. I see from the log level that eclipse link only look inside table from class C. How can I resolve this problem?
Greets Benjamin
here are the classes, class E:
#Entity(name="ep_core_voltagelevel")
public class VoltageLevel extends EquipmentContainer {
#Embedded
#AttributeOverrides(#AttributeOverride(name="value", column=#Column(name="highVoltageLimit_value")
)
)
private myPackage.DomainProfile.Voltage highVoltageLimit;
public myPackage.DomainProfile.Voltage getHighVoltageLimit() {
return highVoltageLimit;
}
public void setHighVoltageLimit(myPackage.DomainProfile.Voltage parameter) {
this.highVoltageLimit = parameter;
}
#Embedded
#AttributeOverrides(#AttributeOverride(name="value", column=#Column(name="lowVoltageLimit_value")
)
)
private myPackage.DomainProfile.Voltage lowVoltageLimit;
public myPackage.DomainProfile.Voltage getLowVoltageLimit() {
return lowVoltageLimit;
}
public void setLowVoltageLimit(myPackage.DomainProfile.Voltage parameter) {
this.lowVoltageLimit = parameter;
}
#ManyToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(nullable=false, name="basevoltage_id")
private BaseVoltage baseVoltage;
public BaseVoltage getBaseVoltage() {
return baseVoltage;
}
public void setBaseVoltage(BaseVoltage parameter) {
this.baseVoltage = parameter;
}
#ManyToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(nullable=false, name="substation_id")
private Substation substation;
public Substation getSubstation() {
return substation;
}
public void setSubstation(Substation parameter) {
this.substation = parameter;
}
}
Class D:
#Entity(name = "ep_core_equipmentcontainer")
public abstract class EquipmentContainer extends ConnectivityNodeContainer {
}
Class B:
#Entity(name="ep_core_connectivitynodecontainer")
public abstract class ConnectivityNodeContainer extends PowerSystemResource {
}
Class A:
public class ConnectivityNode extends IdentifiedObject {
#ManyToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(nullable=false, name="connectivitynodecontainer_id")
private ConnectivityNodeContainer connectivityNodeContainer;
public ConnectivityNodeContainer getConnectivityNodeContainer() {
return connectivityNodeContainer;
}
public void setConnectivityNodeContainer(ConnectivityNodeContainer parameter) {
this.connectivityNodeContainer = parameter;
}
}

Can't fetch certain types of nested objects in Ebean

I'm trying to fetch nested objects in Ebean, but it isn't working. I get the User. It has Addresses. The Addresses each have a House. But the House only has an id. All other properties are null. I read on this other forum that there may be a bug in Ebean, but it was from 2011. Is there a way to make this work?
Note: Address and House have a OneToOne relationship.
Note: I left out #Entity and #Id for simplicity.
public class User {
#OneToMany
public List<Address> addresses;
public static Finder<String, User> find = new Finder(String.class, User.class);
// This is my query
public static Event find(Long id) {
return find.fetch("addresses").fetch("addresses.house").where().eq("id", id).findUnique();
}
}
public class Address {
#OneToOne(cascade = CascadeType.ALL, mappedBy = "address")
public House house;
}
public class House {
#OneToOne
public Address address;
public String somePropertyThatIsNullWhenIUseMyQuery;
}
Ebean.find(User.class).fetch("addresses.house", new FetchConfig().query())
works for me. If you still dont see it, u might want to use
Address.getHouse().getSomeProperty()
Sometimes when u just pass the object to JSON f.e. properties shown as null :(

Audit for OneToOne attributes

Using Glassfish 3.1.2 and eclipselink 2.2.0.
I have to track changes for following entity:
#Entity
#EntityListeners({AuditListener.class})
#Customizer(AuditListener.class)
public class Client extends Person {
...
#OneToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private ConsumptionRoomAndPost consumptionRoomAndPost;
...
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<Document> documentList;
...
AuditListener:
public class AuditListener extends DescriptorEventAdapter implements DescriptorCustomizer {
...
#Override
public void postMerge(DescriptorEvent event) {
if (event.getChangeSet() != null) {
...
}
}
}
This works for a Document list, changeSet is not empty,
but not for ConsumptionRoomAndPost. The changeSet is empty.
Of course I can add own listener ConsumptionRoomAndPostAuditListener for ConsumptionRoomAndPost but for a audit i need a client information and than I have a problem to provide this client information to a ConsumptionRoomAndPostAuditListener.
Solved.
After changing relationships to bidirectional, eclipse link tracks changes for all attributes.
I have added to entity ConsumptionRoomAndPost:
public class ConsumptionRoomAndPost {
...
#OneToOne(mappedBy = "consumptionRoomAndPost", cascade = CascadeType.ALL)
private Client client;
...
}
its all.

How to map childs/parent class with petapoco?

I have these classes:
class Car {
int ID;
string Name;
}
class Truck : Car {
int MaximumLoad;
}
I have these tables
Car
- ID
- Name
Truck
- CarID
- MaximumLoad
How could I map my classes with my table using PetaPoco ?
If you store Car and Truck in the same table (TPH) you can inherit Truck from Car with minor changes to PetaPOCO source code,
table Vehicle (ID, Discriminator, Name, MaximumLoad)
in PetaPOCO.cs, add
[AttributeUsage(AttributeTargets.Class)]
public class DiscriminatorAttribute : Attribute
{
public int Value { get; private set; }
public DiscriminatorAttribute(object discriminator)
{
Value = (int)discriminator;
}
}
protected bool IsTPHTable<T>()
{
var t = typeof(T);
var a = t.GetCustomAttributes(typeof(DiscriminatorAttribute), true);
return a.Length > 0;
}
protected void AppendDiscriminator<T>(Sql sql)
{
var t = typeof(T);
var a = t.GetCustomAttributes(typeof(DiscriminatorAttribute), true);
sql.Append("Where Discriminator = #0", (a[0] as DiscriminatorAttribute).Value);
}
public IEnumerable<T> Query<T>(Sql sql)
{
if (IsTPHTable<T>())
AppendDiscriminator<T>(sql);
return Query<T>(default(T), sql);
}
// also similar AppendDiscriminator() for Update an Delete.
Then in your Car.cs and Truck.cs, you can write/generate code like this,
public enum VehicleType:int
{
Car,
Truck
}
[TableName("Vehicle")]
[Discriminator(VehicleType.Car)]
public class Car
{
[Column]
public int ID {get; set;}
[Column]
public string Name {get; set;}
public Car()
{
//this.Discriminator = VehicleType.Car;
}
public static new Car SingleOrDefault(object primaryKey) { return repo.SingleOrDefaultById<Car>(primaryKey); }
//...
}
[Discriminator(VehicleType.Truck)]
public class Truck:Car
{
[Column]
public double MaximumLoad {get;set;}
public Truck()
{
//this.Discriminator = VehicleType.Truck;
}
public static new Truck SingleOrDefault(object primaryKey) { return repo.SingleOrDefaultById<Truck>(primaryKey); }
//...
}
To read truck records I would create a Trucks view that combines the two tables. Or have a look at Schotime's muliple result sets :
http://schotime.net/blog/index.php/2011/11/20/petapoco-multiple-result-sets/
For writes I guess you are asking "how can I write to 2 tables in one operation". Off the top of my head I would probably say I would simply perform 2 writes. I think Petapoco will ignore fields that don't map so you may be able to use your truck object for both writes.
Could easily be wrong as I haven't tested this.
I think (have not tested though) that if you do something like this..
repo.Fetch<Truck>(";Select car.*, truck.maximumload from car left join truck on car.id = truck.carid");
or
repo.Fetch<Truck>(";Select car.*, truck.maximumload from car left join truck on car.id = truck.carid where truck.carid = #0", truckid);
I would probably have called my base class vehicle rather than car, buts that's just me.
Hope that helps ?