I have some issue with save object with one to many relationship. In my problem One UserGrop has many UserPermissions. Forthat relation ship I have create my domain class like this:
#Entity
#Table(name = "tbl_usergroup")
public class UserGroup implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="userGroupId")
private long userGroupId;
#Column(name = "userGroupName")
private String userGroupName;
#OneToMany(cascade=CascadeType.ALL,mappedBy="userGroup",fetch=FetchType.EAGER)
private Set<UserPermissions> userPermissions = new HashSet<UserPermissions>(0);
}
#Entity
#Table(name = "tbl_group_permissions")
public class UserPermissions implements Serializable {
#Id
#GeneratedValue
private Long userPermissionId;
#ManyToOne(cascade =CascadeType.ALL)
#JoinColumn(name="userGroupId",nullable=false)
#ForeignKey(name = "userGroupId")
private UserGroup userGroup;
}
But When saving UserGroup, it save hat UserPermisions Objects also. There with the table it does not have any relationship(when retrieving UserGroup object, it doesn't return Set of UserPermisions objects).
DB:
+------------------+-------------+
--+
| userPermissionId | userGroupId |
e |
+------------------+-------------+
--+
| 33 | NULL |
|
| 34 | NULL |
|
| 35 | NULL |
|
| 36 | NULL |
|
| 37 | NULL |
|
| 38 | NULL |
|
| 39 | NULL |
|
| 40 | NULL |
|
+------------------+-------------+
--+
8 rows in set (0.00 sec)
Can any body help me to solve this issue?
In the #OneToMany on UserGroup.userPermissions you have mappedBy="userGroup". This means the userGroup property in UserPermisions is responsible for the relation. I guess you don't set that property and upon saving it's still null.
Related
Having this two classes:
Address.java:
#Embeddable
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Address {
private String street;
private String city;
private String state;
private String pincode;
}
User.java:
#Entity
#Data
public class User {
#Id
private int id;
private String name;
#ElementCollection
private Set<Address> addresses = new HashSet<>();
}
DemoApplication.java:
#Bean
CommandLineRunner dataLoader2(UserRepository userRepo){
return new CommandLineRunner() {
#Override
public void run(String... args) throws Exception {
User u = new User();
u.setName("Some random name");
Address a1 = Address.builder()
.street("First Street")
.city("first city")
.state("first state")
.pincode("100001")
.build();
Address a2 = Address.builder()
.street("Second Street")
.city("Second city")
.state("second state")
.pincode("200002")
.build();
u.setAddresses(new HashSet<>(Arrays.asList(a1, a2)));
userRepo.save(u);
}
};
}
When run, if fails with this error:
GenerationTarget encountered exception accepting command : Error
executing DDL "alter table user_addresses drop foreign key
FKfm6x520mag23hvgr1oshaut8b" via JDBC Statement
Yet, the final tables are created:
describe user_addresses:
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| user_id | int | NO | MUL | NULL | |
| city | varchar(255) | YES | | NULL | |
| pincode | varchar(255) | YES | | NULL | |
| state | varchar(255) | YES | | NULL | |
| street | varchar(255) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
describe user:
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int | NO | PRI | NULL | |
| name | varchar(255) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
Why cannot the jdbc drop the foreign key user_id in table user_addresses? How to make the jdbc to do so?
"USER" is a reserved word in MySQL and that might be the root cause of the issue. Change the name of the User entity or add #Table annotation to it and define a different name.
Reference documentation:
https://dev.mysql.com/doc/refman/5.7/en/keywords.html
https://dev.mysql.com/doc/refman/8.0/en/keywords.html
The environment is Java, Spring-boot, Hibernat, QueryDSL, MySQL.
I have table structure
Episode
+----+-------------+--------
| id | address_id | eventno
+----+-------------+--------
| 5 | 27 | F123
| 6 | 30 | F456
| 7 | 45 | F789
+----+-------------+--------
#Entity
public class Episode {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty
private String eventno;
#ManyToOne(cascade = CascadeType.ALL)
private Address address;
Episode_Person
+----+--------------+--------------+------------+-----------+
| id | episode_role | primary_flag | episode_id | person_id |
+----+--------------+--------------+------------+-----------+
| 19 | Buyer | | 5 | 1 |
| 20 | Subject | | 5 | 2 |
| 23 | Witness | | 6 | 3 |
| 24 | Child | | 6 | 4 |
| 27 | Buyer | | 5 | 3 |
| 63 | Investor | | 5 | 4 |
| 64 | Subject | | 7 | 1 |
| 65 | Subject | | 7 | 3 |
#Entity
public class EpisodePerson {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#Valid
private Person person;
#ManyToOne
private Episode episode;
Person
+----+-----------+----------+
| id | firstname | surname |
+----+-----------+----------+
| 1 | Clint | eastwood |
| 2 | Angelina | joilee |
| 3 | Brad | pitt |
| 4 | Jennifer | aniston |
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = {"nia"}))
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String surname;
private String firstname;
private String gender;
So each episode has multiple people. And the join table is Episode_Person.
My UI has a datatable with a filter on each column:
The filtering already works on Event and Address. And looks like this predicate in QueryDSL:
BooleanBuilder where = new BooleanBuilder();
if (pagination.getFilterBy().getMapOfFilters().get("eventno")!=null) {
where.and(qEpisode.eventno.containsIgnoreCase(pagination.getFilterBy().getMapOfFilters().get("eventno")));
}
if (pagination.getFilterBy().getMapOfFilters().get("address")!=null) {
where.and(qEpisode.address.formattedAddress.containsIgnoreCase(pagination.getFilterBy().getMapOfFilters().get("address")));
}
where.and(qEpisode.creatingUser.eq(user));
List<Episode> e = episodeRepository.findAll(where);
How would I now add a 3rd predicate for case name where case name is constructed of the first two people returned in the collection of people against a episode?
UPDATE
For clarification the DTO thats backs the UI view contains the "casename" attribute. It is created in the service layer when Domain objects are converted to DTO:
episodeDashboard.setNames(episodePersonList.get(0).getPerson().getSurname().toUpperCase() +" & " +episodePersonList.get(1).getPerson().getSurname().toUpperCase());
Not easily unless you delegate some of the processing to the database.
If we can get the case_name property to be populated at the database tier rather than as a derived property in the application logic then the front-end code becomes trivial.
We can do this by means of a view. The exact definition of this will depend on your database however the output would be something like this:
episode_summary_vw
+------------+-------------------------+
| epsiode_id | case_name |
+------------+-------------------------+
| 5 | Eastwood & Joilee|
| 6 | Pitt & Aniston|
| 7 | Aniston & Pitt|
+------------+-------------------------+
For Oracle it looks like LISTAGG function is what you would want and for MySQL the GROUP_CONCAT functions. In MySQL then I think this would look something like:
CREATE VIEW episode_summary_vw as
SELECT ep.episode_id, GROUP_CONCAT(p.surname SEPARATOR ' & ')
FROM episode_person ep
INNER JOIN person p on p.id = ep.person_id
GROUP BY ep.episode_id;
-- todo: needs limit to first 2 records
Once we have a view then we can simply map the case_name to the Episode entity using the #SecondaryTable functionality of JPA:
#Entity
#Table(name = "episodes")
#SecondaryTable(name = "episode_summary_vw", primaryKeyJoinColumna = #PrimaryKeyJoinColumn(name="episode_id", reference_column_name="id"))
public class Episode {
#Column(name ="case_name", table = "episode_summary_vw")
private String caseName;
}
You then filter and sort on the property as for any other field:
if (pagination.getFilterBy().getMapOfFilters().get("caseName")!=null) {
where.and(qEpisode.caseName.containsIgnoreCase(pagination.getFilterBy().
getMapOfFilters().get("caseName")));
}
I've got a MySQL database schema with 3 tables as follows:
mysql> describe results;
+--------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| run_at | datetime | YES | | NULL | |
| trials | int(10) unsigned | YES | | NULL | |
+--------------+------------------+------+-----+---------+----------------+
mysql> describe result_details;
+------------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| results_id | int(10) | NO | | NULL | |
| summarys_id | int(10) | YES | | NULL | |
+------------------------+------------------+------+-----+---------+----------------+
mysql> describe summarys;
+-------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+----------------+
| mean | double | YES | | NULL | |
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
+-------+------------------+------+-----+---------+----------------+
Where a Result object can have several ResultDetail members. However, semantically, it makes sense to have one of these details stand out among the rest as a 'overall' detail. Therefore, I have the following classes:
Result.java (some members and methods removed for brevity)
#Entity
#Table(name="results")
public class Result extends BaseEntity {
#Column(name="run_at")
#Temporal(TemporalType.TIMESTAMP)
private Date runAt;
#Column(name="trials")
private Integer trials;
#OneToOne(mappedBy="result", cascade = {CascadeType.ALL})
private ResultDetails overallStats;
#OneToMany(mappedBy="result", cascade = {CascadeType.ALL})
private List<ResultDetails> resultDetails = new ArrayList<ResultDetails>();
}
ResultDetail.java
#Entity
#Table(name="result_details")
public class ResultDetails extends BaseEntity {
#ManyToOne
#JoinColumn(name = "results_id", nullable=false)
#NotNull
private Result result;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="summarys_id", nullable=true)
private Summary summary;
}
When I create persistent entities from my main as follows:
public static void main (String [] args) {
Result result = new Result();
ResultDetails detail1 = new ResultDetails();
ResultDetails detail2 = new ResultDetails();
Summary s1 = new Summary();
Summary s2 = new Summary();
result.setRunAt(new Date());
result.setTrials(1000000);
detail1.setResult(result);
s1.setMean(3.0);
detail1.setSummary(s1);
result.setOverallStats(detail1);
detail2.setCybervarResult(result);
s2.setMean(11.0);
detail2.setSummary(s2);
result.addDetails(detail2);
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
session.saveOrUpdate(result);
session.getTransaction().commit();
HibernateUtil.shutdown();
}
It adds the appropriate rows to the tables. However, when I retrieve the data as demonstrated by the following additional main file:
Session session = HibernateUtil.getSessionFactory().openSession();
Result result = session.get(CybervarResult.class, 6);
result.getOverallStats().getSummary();
result.getResultDetails().size();
HibernateUtil.shutdown();
System.out.println(result.getOverallStats().getSummary().getMean());
System.out.println(result.getResultDetails().get(0).getSummary().getMean());
Hibernate is able to correctly populate the 'overallStats' and 'resultDetails' objects. How is it able to differentiate the two rows in the result_details table? As far as I can tell, there is nothing to distinguish the two from each other. Does Hibernate implement hidden tables/rows to manage which member variables correspond to which rows? If I were to create this database from mysql queries instead of through the hibernate API, how would Hibernate know which row should be the 'overallStats' and which rows should belong to the 'resultDetails' collection?
For reference, the rows created look as follows:
mysql> select * from results;
+----+---------------------+---------+
| id | run_at | trials |
+----+---------------------+---------+
| 6 | 2017-11-13 09:27:52 | 1000000 |
+----+---------------------+---------+
mysql> select * from result_details;
+----+------------+-------------+
| id | results_id | summarys_id |
+----+------------+-------------+
| 10 | 6 | 14 |
| 11 | 6 | 15 |
+----+------------+-------------+
hibernate.cfg.xml
<property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/usersdb</property>
<property name="hibernate.connection.username">test</property>
<property name="hibernate.connection.password">test</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">validate</property>
We have Hibernate based application where due to a large data set, two sets of tables are created where user_id will either be mapped in the UserTickets table or RestOfWorldTickets table.
Would like to know how #Table on the entity java objects can be dynamically mapped based on some user selection.
#Entity
#Table(name = "**UserTickets**")
public class UserTickets {
#Id
#Column("Internal_id")
#GeneratedValue(strategy = IDENTITY)
private int internalId;
#Column("user_id")
private int userId;
#Column("state")
private String state;
#Column("city")
private String city;
#Column("address")
private String address;
#Column("ticketNumber")
private String ticketNumber;
..
// Setters and Getters
}
UserTickets DB Table
Internal_id | User_id | State | City | Address | ticket_number | ...
101 | 1025 | AZ | Tuscan | .. | 10256912 |
102 | 1026 | NC | Durham | .. | 10256983
RestOfWorldTickets DB Table
Internal_id | User_id | State | City | Address | ticket_number |..
101 | 1058 | {null} | London | .. | 102578963 |..
102 | 1059 | {null} | Berlin | .. | 112763458 |..
The user and table mapping are now defined in a new table.
TableMapping Database table.
Internal_id | User_id | TableMapped |
1 | 1025 | UserTickets |
2 | 1026 | UserTickets |
3 | 1058 | RestOfWorldTickets |
4 | 1059 | RestOfWorldTickets |
So, using the UserTickets result set, how I map #Table attribute on the UserTickets Java object dynamically so that my Criteria API queries will work automatically without changing them to HQL queries?
Maybe using Interceptors http://docs.jboss.org/hibernate/orm/3.5/javadocs/org/hibernate/Interceptor.html?
I am quite unsure what you actually need but i try to give my solution based on a few quesses. Changing #Table dynamically is not -afaik- possible but if i guessed right you could have some benefit of inheritance in this case:
1st modify UserTickets to allow inheritance
#Entity
//#Table(name = "**UserTickets**")
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class UserTickets {
#Id // this annotation was missing from yours ?
#Column(name="Internal_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE)
// identity generated problems in openjpa so i changed it to SEQUENCE
private int internalId;
#Column(name="user_id") private int userId;
#Column(name="state") private String state;
#Column(name="city") private String city;
#Column(name="address") private String address;
#Column(name="ticketNumber") private String ticketNumber;
}
2nd create a new entity
#Entity
public class RestOfWorldTickets extends UserTickets {
// yes i am just an empty class, TABLE_PER_CLASS gives me the fields
}
This allows you to use criteriaqueries against UserTickets but in addition the queries are done against RestOfWorldTickets also. So now when you search with user id result set will contain results from both tables. Checking/ loggin -for example with instanceof operator- you can see which one the ticket is.
"Disclaimer" i am using and testing with openjpa so there can be some differences/probkems with this solution...
The GenericVehicle represent a vehicle which may have 0 or more GenericVehicleAccessory.
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class GenericVehicle extends GenericEntity {
#Id
private Long id;
#Column(nullable=false)
#NotNull(message="{GenericVehicle.vehicleName.notNull")
#Size(max=128, message="{GenericVehicle.vehicleName.size")
private String vehicleName;
#OneToMany(mappedBy = "genericVehicle", cascade = { CascadeType.ALL }, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<GenericVehicleAccessory> accessories;
// ..
}
#Entity
#Table(name = "GENERICVEHICLEACCESSORIES")
public class GenericVehicleAccessory extends GenericEntity {
#Id
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "GENERICVEHICLE_ID")
private GenericVehicle genericVehicle;
private String name;
// ..
}
I would like to perform a query which selects all GenericVehicle(s) which owns a Set (accessories) of a given criteria set.
For example:
+----+-------------------+
| ID | VEHICLENAME |
+----+-------------------+
| 1 | Toyota Auris |
| 2 | Volkswagen Passat |
| 3 | Bentley Arnage |
| 4 | Hyundai Accent |
| 5 | Toyota Auris |
+----+-------------------+
+-------------------+----------------------------------+
| GENERICVEHICLE_ID | NAME |
+-------------------+----------------------------------+
| 1 | Leather seats |
| 1 | Electronic stability control |
| 2 | Power steering |
| 4 | ABS |
| 4 | Airbag |
| 4 | Cruise control |
| 5 | Leather seats |
| 5 | Electronic stability control |
+-------------------+----------------------------------+
A criteria like this:
criteria.setVehicleName="Toyota Auris";
new GenericVehicleAccessory tmp0 = new GenericVehicleAccessory();
tmp0.setName="Leather Seats";
new GenericVehicleAccessory tmp1 = new GenericVehicleAccessory();
tmp1.setName="Electronic stability control";
criteria.addToAccessory(tmp0);
criteria.addToAccessory(tmp1);
would select the GenericVehicle entity with ID=1 (since ID=5 not have Electronic Stability Control).
How can I do that by using JPA Criteria API?