My application works with 2 databases: 1 SQL Server and 1 PostgreSQL.
The SQL Server is used for the website and had some performance issues in the past, therefore, I separated some analysis data into a different database, where I am adding data constantly and calculate some things all day long.
In most of the cases I use the databases separately, but in one case I would like to join two tables from different databases.
Is there any way I can do this with Hibernate?
To exemplify, I have this two simplified entities: Order and OrderSource.
The Order Table is in the SQL Server and OrderSource is in the PostgreSQL.
They both work fine by themselves, but when I added the OrderSource to the entity Order and Order to the entity OrderSource, it obviously went wrong, because they are trying to access a database that is configured in another SessionFactory.
Order Entity:
#Entity
#Table(name=DomainConstants.TB_ORDER, schema=DomainConstants.DB_SCHEMA)
public class Order {
#Id
#Column(name="cod")
#GeneratedValue
private Long id;
#OneToOne(mappedBy="order", fetch=FetchType.LAZY)
private OrderSource orderSource;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public OrderSource getOrderSource() {
return orderSource;
}
public void setOrderSource(OrderSource orderSource) {
this.orderSource = orderSource;
}
}
OrderSource Entity:
#Entity
#Table(name=DomainConstants.TB_ORDER_SOURCE, schema=DomainConstants.DB_SCHEMA_GOOGLE)
public class OrderSource {
#Id
#Column(name="id")
private long id;
#Column(name="source")
private String source;
#Column(name="medium")
private String medium;
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="id")
private Order order;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getMedium() {
return medium;
}
public void setMedium(String medium) {
this.medium = medium;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
One solution I could come up with was to work on my OrderService with my OrderDao and my OrderSourceDao.
This way I removed the mapping from each of the entities and created one Service to get all Orders and iterate over all of them getting the OrderSource.
This is, however, not as fast as I would like and I am not able to group data or order them.
#Component
public class OrderServiceImpl implements OrderService {
private OrderDao orderDao;
private OrderSourceDao orderSourceDao;
#Autowired
public OrderServiceImpl(OrderDao orderDao, OrderSourceDao orderSourceDao) {
this.orderDao = orderDao;
this.orderSourceDao = orderSourceDao;
}
public List<Order> getOrdersWithOrderSource() {
List<Order> orders = this.orderDao.index(); // get all Orders
for(Order order: orders)
order.setOrderSource(this.orderSourceDao.findById(order.id)); // Find OrderSource by Order id
}
}
Has anyone come across something like this? Does anyone have a solution for this join on different databases?
Thanks so much everyone!
Have you considered using an SQL query to join the tables over the DB link and then via ResultSetHandler map it to the entities (if it's just for view purposes) or based on this retrieve set of IDs in the DB where you want to make the change and then select the entities through HQL and ID set so you could store them to your main DB (I am assuming your main DB where you would want to change the data is the web one and not the analytical one)?
Related
I'm trying to implement a reactive(r2dbc) repository in Micronaut but I'm having some problems with the data that is being queried. Those issues don't occur when using non-reactive repositories.
Here's how my reactive repository looks:
#R2dbcRepository(dialect = Dialect.MYSQL)
public interface ReactiveCampaignRepository extends ReactiveStreamsCrudRepository<Campaign, Integer> {
#Override
Flux<Campaign> findAll();
}
And this is how my regular repository looks:
#Repository
public interface CampaignRepository extends CrudRepository<Campaign, Integer> {
}
When invoking findAll method from ReactiveCampaignRepository I'm able to query all entities, however all of them have null ids.
When I invoke findAll from CampaignRepository all entites are queried and Ids are populated correctly.
This is how id field looks in Campaign, which is a remote dependency
#Entity
#Table(name = "campaign")
public class Campaign implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
This entity is introspected like this:
#Introspected(classes = {Campaign.class})
public class EntitiesConfiguration {
}
I'm new to micronaut and r2dbc so I could be missing something obvious but I cannot figure it out and any pointers would be greatly appreciated.
Thank You
EDIT:
#tmarouane It's just a simple controller to test if things are working as they should.
#Get(value = "/all")
public Flux<CampaignDTO> allCampaigns() {
return reactiveCampaignRepository.findAll().map(CampaignDTO::new);
}
#Get(value = "/all2")
public List<CampaignDTO> allCampaigns2() {
return StreamSupport.stream(campaignRepository.findAll().spliterator(), false).map(CampaignDTO::new).collect(Collectors.toList());
}
and controller
#Produces(MediaType.APPLICATION_JSON)
#Secured(SecurityRule.IS_AUTHENTICATED)
#Controller("/campaign")
public class CampaignController {
private final CampaignRepository campaignRepository;
private final ReactiveCampaignRepository reactiveCampaignRepository;
public CampaignController(CampaignRepository campaignRepository,
ReactiveCampaignRepository reactiveCampaignRepository
) {
this.campaignRepository = campaignRepository;
this.reactiveCampaignRepository = reactiveCampaignRepository;
}
CampaignDTO is just a simple DTO class where just a subset of Campaign's fields are used, with a simple constructor taking Campaign object.
public CampaignDTO(Campaign campaign) {
this.id = campaign.getId();
}
Besides id there's 1 more attribute which is not null but it's own attributes are null which I haven't spotted at first - customer, even though customer_id is populated in objects queried with both reactive and non reactive repos, here's how it looks in Campaign
#JoinColumn(name = "customer_id", referencedColumnName = "customer_id")
#ManyToOne(optional = false)
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
This seems to be solved in micronaut 3.0.1 but it doesn't work in 3.0.2
Hi I have a confusing error with my Spring-boot-JPA-Hibernate classes.
I used the JPA Dali tools to create the Entity classes from my SQL Schema.
While using them (with Spring-boot-jpa / hibarnate) I have some strange problems with unmatched queries.
Here is one example:
Properties:
hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:mysql://localhost:3306/users
spring.datasource.username=root
spring.datasource.password=root
Entity:
#Entity
#Table(name="n_user")
#NamedQuery(name="NUser.findAll", query="SELECT n FROM NUser n")
public class NUser implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private int id;
private String imageUrl1;
private String name_first;
public NCaterer() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getImageUrl1() {
return this.imageUrl1;
}
public void setImageUrl1(String imageUrl1) {
this.imageUrl1 = imageUrl1;
}
public String getName_first() {
return this.name_first;
}
public void setName_first(String name_first) {
this.name_first = name_first;
}
}
Repository:
public interface UserRepo extends CrudRepository<NUser, Long> {
}
But Hibernate creates this query of it:
Hibernate:
/* select
generatedAlias0
from
NUser as generatedAlias0 */ select
nuser0_.id as id1_0_,
nuser0_.image_url1 as image_ur2_0_,
nuser0_.name_first as name_firs3_0_
from
n_user nuser0_
The Problem here is the image_url1 it should be imageurl1 as described in the Entity class. This happens on several points.
Why do Hibernate transform CamelCase getters into camel_case in the query? And how can I config it not to do it?
If you want to specify column name by your own use #Column annotation for your fields.
Example:
#Column(name = "imageUrl1")
private String imageUrl1;
#Column(name = "nameFirst")
private String name_first;
It seems there have been many people experiencing the same problem in different circumstances. Maybe go and have a look at the different naming strategies below:
Naming Strategy One
Naming Strategy Two
Naming Strategy Three
You can however change the configuration of your hibernation. Have a look here for further help with regards to that. Or, go and have a look at this post and see how someone changed it using the above methods.
I am searching for a design solution to the problem where I have 2 classes which depend on each other such that I have class Customer and class Order where:
Customer can have a list of orders (1-to-N) and an Order has a designated customer (1-to-1).
What is the best practice to break these kind of dependencies?
Assuming you have a dependency as follows:
public class Customer {
private long customerId;
private String name;
private String address1;
// ....
private List<Order> orders;
}
public class Order {
private long orderNumber;
private Date orderDate;
// ... others
private Customer customer;
}
You could create a third class to break the dependency:
public class CustomerOrder {
private final Customer customer;
private final List<Order> orders;
public CustomerOrder(Customer customer) {
super();
this.customer = customer;
this.orders = new ArrayList<Order>();
}
public void addOrder(Order order) {
orders.add(order);
}
public Customer getCustomer() {
return customer;
}
public List<Order> getOrders() {
return orders;
}
}
Now you can drop orders from the Customer class, and customer from the Order class. Or am I misunderstanding your issue?
As a software engineer for approx 2 years, the best I’ve seen for a case like that is to put a shadow definition of one class, without initializing it to anything, simply telling the compiler “hey orders exist”, then defining the other class explicitly, followed by your orders class explicitly. Does that get you in the right direction? Nodes and trees sometimes are modeled this way, and data structure and analysis of algorithms books tend to have decent design solutions to this too.
pals.
I have an issue with Hibernate's JPA implementation. I use spring-boot-starter-data-jpa and PostgreSql v9.
I have two entities with bidirectional connection via OneToMany & ManyToOne:
#Entity
public class ShoppingCart {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "shoppingCart", cascade = {CascadeType.ALL})
private List<Good> goods = new ArrayList<>();
public void addGood(Good good) {
good.setShoppingCart(this);
goods.add(good);
}
public Good removeGood(Good good) {
goods.remove(good);
good.setShoppingCart(null);
return good;
}
public ShoppingCart() {
}
public List<Good> getGoods() {
return goods;
}
public ShoppingCart(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
And second entity is
#Entity
public class Good {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cart_id")
#JsonIgnore
private ShoppingCart shoppingCart;
public ShoppingCart getShoppingCart() {
return shoppingCart;
}
public void setShoppingCart(ShoppingCart shoppingCart) {
this.shoppingCart = shoppingCart;
}
public Good(String name) {
this.name = name;
}
public Good() {
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
Also I use CrudRepository to access ShoppingCart
public interface ShoppingCartRepository extends CrudRepository<ShoppingCart, Long> {}
And when I'm trying to fill existing cart I have two goods in my database. This is a code to add some goods into existing cart:
ShoppingCart cart = shoppingCartRepository.findOne(id);
cart.addGood(new Good("Butter"));
return shoppingCartRepository.save(cart);
In table "good" I have now two elements with different PKey and same data
5;"Butter";100
6;"Butter";100
Why it happens?
Also, when I'm trying to insert breakpoint at repository.save line, I see only one good in goods list in cart.
So, the problem is solved.
First way to solve is to make method with save code #Transactional.
Secon way is to use getGoods() instead of goods. We should change this code
public void addGood(Good good) {
good.setShoppingCart(this);
goods.add(good);
}
public Good removeGood(Good good) {
goods.remove(good);
good.setShoppingCart(null);
return good;
}
to this
public void addGood(Good good) {
good.setShoppingCart(this);
this.getGoods().add(good);
}
public Good removeGood(Good good) {
this.getGoods().remove(good);
good.setShoppingCart(null);
return good;
}
getGoods() here forces hibernate to update state of object and everything works fine.
As for me, I use both ways together
It happens because you create a new Good object without id. So Hibernate will generate a new id and persist the new object. If you don't want to create a new object, but only assign an already existing one, you either have to fetch the existing one from the database and assign it to the ShoppingCart oder add the ID if you create the new Good object.
I am using hibernate for entity persistence in application however when i use HQL to fetch entities it works fine and returns the exact number of entities that is there in a table however when i use the hibernate's criteria with Example it return first entity twice and thus results in returning one entity more then what is actually there in the table
Entity Definition
public class ItemParentCategory {
#Id
#GeneratedValue
private long id;
private String name;
#OneToMany(mappedBy = "itemParentCategory",fetch = FetchType.EAGER)
#JsonIgnore
private List<ItemSubCategory> itemSubCategorys;
#OneToOne
private ItemMainCategory itemMainCategory;
#JsonIgnore
private String summary;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ItemSubCategory> getItemSubCategorys() {
return itemSubCategorys;
}
public void setItemSubCategorys(List<ItemSubCategory> itemSubCategorys) {
this.itemSubCategorys = itemSubCategorys;
}
public ItemMainCategory getItemMainCategory() {
return itemMainCategory;
}
public void setItemMainCategory(ItemMainCategory itemMainCategory) {
this.itemMainCategory = itemMainCategory;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
}
Code For Fetching Entities
#Override
public List<ItemParentCategory> getAllItemParentCategoryUnderItemMainCategory(long itemMainCategoryId) {
//This code works fine
Query query = sessionFactory.getCurrentSession().getNamedQuery("ItemParentCategory.getAllItemParentCategoryUnderItemMaincategory");
query.setParameter("itemMainCategoryId", itemMainCategoryId);
return query.list();
//This Code return undesired result
/*Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ItemParentCategory.class);
ItemParentCategory itemParentCategory = new ItemParentCategory();
ItemMainCategory itemMainCategory = new ItemMainCategory();
itemMainCategory.setId(itemMainCategoryId);
itemParentCategory.setItemMainCategory(itemMainCategory);
Example example = Example.create(itemParentCategory);
criteria.add(example);
return criteria.list();*/
}
HQL I used
FROM ItemParentCategory itemParentCategory WHERE itemParentCategory.itemMainCategory.id = :itemMainCategoryId
What is happening here is as a result of the fact that you have mapped ItemParentCategory to ItemSubcategory as FetchType.EAGER.
Now, this does not affect your HQL query as HQL queries will not respect the EAGER mapping and to eager fetch the sub categories in HQL you would have to explicitly define a FETCH JOIN in your query. Therefore everything is fine in this scenario.
If your changed your HQL to be as below you would most have the same duplicates issue:
FROM ItemParentCategory ipc JOIN FETCH ipc.itemSubCategorys WHERE ipc.itemMainCategory.id = :itemMainCategoryId
See the following two FAQs:
https://developer.jboss.org/wiki/HibernateFAQ-AdvancedProblems#jive_content_id_Hibernate_does_not_return_distinct_results_for_a_query_with_outer_join_fetching_enabled_for_a_collection_even_if_I_use_the_distinct_keyword
https://developer.jboss.org/wiki/HibernateFAQ-AdvancedProblems#jive_content_id_Hibernate_ignores_my_outerjointrue_or_fetchjoin_setting_and_fetches_an_association_lazily_using_n1_selects
HQL queries always ignore the setting for outer-join or fetch="join"
defined in mapping metadata. This setting applies only to associations
fetched using get() or load(), Criteria queries, and graph navigation
Query by Example then must work in a similar way to Criteria Queries and where you have fetch = FetchType.EAGER Hibernate is doing an explicit join. You should turn on SQL logging to view the actual SQL generated in each case.
try put a distinct in your query
query.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);