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
Related
I saw this kind of API style once and it worked proper
Noob here and I am current learning RESTful stuff,If anyone who may gives some advice and instruction.I'd be very appreciate!
Get URL
//The argument isn't mandatory, May be order.orderInfo,order.orderPrice etc
http://localhost:8080/order?order.orderNo=123
Controller code
#GetMapping
CollectionModel<Order> getOrders(Order order) {
List<Order> ordersResult = orderService.getOrders(order);
for (Order result : ordersResult) {
Link selfLink = WebMvcLinkBuilder.linkTo(OrderController.class).slash(result.getId()).withSelfRel();
result.add(selfLink);
}
Link link = WebMvcLinkBuilder.linkTo(OrderController.class).withSelfRel();
return CollectionModel.of(ordersResult, link);
}
Entity code
public class Order extends RepresentationModel<Order> implements Serializable {
#Id
#GeneratedValue
#Column(unique = true)
private Integer id;
private Long orderNo;
}
And my jpa repository
public interface OrderRepository extends JpaRepository<Order, Integer>,PagingAndSortingRepository<Order, Integer> {
}
I have finally figured out that this kind of URL is not work this way.
Shoud be apply to a specific situation.Which is the get endpoint method has a 'Relative Object'.Then we may use URL like this to improve flexable api.
Talk is cheap,I'll show you the code!
Entity->Customer
public class Customer extends RepresentationModel<Customer> implements Serializable {
#Id
#GeneratedValue
#Column(unique = true)
private Integer id;
private String name;
//Add relative Object to entity.
#OneToOne
private Order order;
}
Entity->Order
public class Order extends RepresentationModel<Order> implements Serializable {
#Id
#GeneratedValue
#Column(unique = true)
private Integer id;
private Long orderNo;
//Add relative Object to entity.
#OneToOne
private Customer customer;
}
Endpoint->CustomerController/getMethod
#GetMapping
CollectionModel<Customer> getCustomers(Customer customer) {
List<Customer> customersResult =
customerService.getCustomers(customer);
for (Customer result : customersResult) {
Link selfLink = WebMvcLinkBuilder.linkTo(CustomerController.class).slash(result.getId()).withSelfRel();
result.add(selfLink);
}
Link link = WebMvcLinkBuilder.linkTo(CustomerController.class).withSelfRel();
return CollectionModel.of(customersResult, link);
}
URL
scheme://[api]/[order.orderNo=123]
Then the value will mapping to paramter-custoemr inside.
PS:HTTP method '#fragment' may suitable for this case.
Thanks to #JRichardsz :)
I have defined customer entity
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
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;
}
}
and CrudRepository
public interface CustomerRepo extends CrudRepository<Customer, Long> {
}
if I use CustomerRepo.findById method for finding Customer
#Autowired
CustomerRepo repo;
Optional<Customer> dbCustomer = repo.findById(id);
how can I get name of that customer. I cannot use getter then.
so I'm interested is there any solution of using getters of Optional, or I need to use other method for finding Customer by id?
Optional<Customer> is returned, because it is not guaranteed that there will be such a customer with the requested ID in the database.
Instead of returning null it simply means that Optional.isPresent() will return false when the ID does not exist.
According to the API Docs (https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html#findById-ID-):
Returns:
the entity with the given id or Optional#empty() if none found
You will therefore probably want to simply use the methods on Optional to check whether it contains a Customer (i.e. a Customer with that ID exists), and then get the name like so:
Optional<Customer> dbCustomer = repo.findById(id);
if(dbCustomer.isPresent()) {
Customer existingCustomer = dbCustomer.get();
String nameWeWanted = existingCustomer.getName();
//operate on existingCustomer
} else {
//there is no Customer in the repo with 'id'
}
Alternatively you can try callback style (shown with Java 8 Lambda):
Optional<Customer> dbCustomer = repo.findById(id);
dbCustomer.ifPresent(existingCustomer -> {
String nameWeWanted = existingCustomer.getName();
//operate on existingCustomer
});
It is worth noting that it is possible to check existence of the ID without actually retrieving/loading the entity by using the interface method:
boolean CrudRepository.existsById(ID id)
This saves an entity load, but it still requires a database roundtrip.
Try to use another method for finding Customer:
#Autowired
CustomerRepo repo;
Customer dbCustomer = repo.findOne(id);
I try to get simple List of Rawtype entities with help of findBy method in the myMethod. But I get nothing - rawtypes doesn't contain any entity. Although findAll method works fine. Please tell we where is my mistake.
Rawtype.java
#Entity
#Table(name="rawtype")
public class Rawtype implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="rtid", nullable = false)
#GeneratedValue
private int rtId;
#Column(name="rtname", nullable = false)
private String rtName;
//getters and setters
RawtypeRepository.java
public interface RawtypeRepository extends JpaRepository<Rawtype, Integer> {
List<Rawtype> findByRtName(String rtName);
}
RawtypeServiceImpl.java
#Service
#Transactional
public class RawtypeServiceImpl implements RawtypeService {
#Autowired
RawtypeRepository rawtypeRepository;
public List<Rawtype> findAll() {
return rawtypeRepository.findAll();
}
public myMethod(){
List<Rawtype> rawtypes = rawtypeRepository.findByRtName("RawName");
}
}
Can you try printing rtName of all the entities returned by findAll() method? May be there isn't any record with 'RawName' as rtName.
Also, you can enable logging for JPA to see the generated query.
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.
This is my first post here, I've been searching for a long time here but I didn't found a problem that seemed similar.
When I use JpaRepository function findOne(id) for one of my classes, it returns null. As if no row had been found for this id.
Of course the database row with this id exists.
Also my class mapping seems right.
I don't understand because I already used findOne() for other classes and I never had any problem.
Anyone can tell me what can be the source of this problem, please ? That would be nice !
This is my DAO :
#Transactional
public interface OrderDetailDAO extends JpaRepository<OrderDetail, Integer>
{
}
This is my Model :
#Entity
#Table(name = "order_detail", schema = "", catalog = AppConfig.databaseSchema)
public class OrderDetail implements Serializable {
private int idOrderDetail;
private Order order;
private Preorder preorder;
private UnitType unitType;
private Sale sale;
private DeliveryStatusType deliveryStatusType;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_Order_Detail")
public int getIdOrderDetail() {
return idOrderDetail;
}
public void setIdOrderDetail(int idOrderDetail) {
this.idOrderDetail = idOrderDetail;
}
#ManyToOne
#JoinColumn(name = "id_Order", referencedColumnName = "id_Order", nullable = false)
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
#ManyToOne
#JoinColumn(name = "id_Preorder", referencedColumnName = "id_Preorder", nullable = false)
public Preorder getPreorder() {
return preorder;
}
public void setPreorder(Preorder preorder) {
this.preorder = preorder;
}
#ManyToOne
#JoinColumn(name = "id_Unit_Type", referencedColumnName = "id_Unit_Type")
public UnitType getUnitType() {
return unitType;
}
public void setUnitType(UnitType unitType) {
this.unitType = unitType;
}
#ManyToOne
#JoinColumn(name = "id_Sale", referencedColumnName = "id_Sale")
public Sale getSale() {
return sale;
}
public void setSale(Sale sale) {
this.sale = sale;
}
#ManyToOne
#JoinColumn(name = "id_Delivery_Status_Type", referencedColumnName = "id_Delivery_Status_Type")
public DeliveryStatusType getDeliveryStatusType() {
return deliveryStatusType;
}
public void setDeliveryStatusType(DeliveryStatusType deliveryStatusType) {
this.deliveryStatusType = deliveryStatusType;
}
}
When I write a request manually, like this :
#Query("SELECT o FROM OrderDetail o WHERE o.idOrderDetail = :idOrderDetail")
public OrderDetail findOneCustom(#Param("idOrderDetail") Integer idOrderDetail);
That works, but that's ugly so I would prefer to use JpaRepository native function findOne()
After all investigation, I have found an interesting answer that is worked for me. I think it is all about defining column type on Db. For my case, I have defined the variable (rid as column) as varchar2(18) that was RID CHAR(18 BYTE).
Java part:
if (dhFlightRepo.findOneFlight(dhFlight.getRid())== null) {
dhFlightRepo.save(dhFlight);
}
If your value that you used as a parameter for findOne() is smallest than set value on column (18 for my case),the jpa doesn't accept value and returns null.You have to change column type as varchar2(18) it can be changeable according to given value on findOne() and work perfect.
I hope that works for all of you.I kindly request to give more detail If someone knows the reason with more detail.