Small question regarding how to save a Mono into a particular reactive table please.
I have a very simple piece of code:
#Override
public Mono<MyPojo> question() {
return myPojoRepository.insert(someService.getMonoMyPojo());
}
where MyPojo is just a POJO with a state represented by a String:
#Table
public class MyPojo {
private String state;
The service just computes a MyPojo, with a state "good" or "bad"
and finally, any reactive repository for saving the MyPojo.
#Repository
public interface MyPojoRepository extends ReactiveCassandraRepository<MyPojo, String> {
}
My reactive database have three tables, table 1, mygoodpojo table, table two, mybadpojo table, and mypojo table.
What I would like to achieve, is depending on the state of MyPojo the service computes, to save in the good or bad table.
As of now, with the repository as
public interface MyPojoRepository extends ReactiveCassandraRepository<MyPojo, String> {
It is saving all MyPojo, regardless of the state, into the mypojotable.
May I ask how to perform the use case where the good MyPojo is saved inside mygoodpojo, and the bad MyPojo saved inside mybadpojo please?
In a non reactive world, I would have written something like:
if (myPojo.getState().equals("good")) {
Statement statement = Insert into mygoodpojo [...];
} else {
Statement statement = Insert into mybadpojo [...];
}
But I am having a hard time doing the same with this reactive stack.
Any help please?
Thank you!
Related
I have tried the java records in new code. It looks nice but I face a trouble when deserializing from JSON in Spring REST controller.
Record
public record Order(UUID uuid, List<OrderItem> items) {}
Controller
#PostMapping(value = "/allocations")
public Allocation allocate(Order order) throws Exception {
return planningService.allocate(order);
}
Request:
{
"uuid": "123e4567-e89b-12d3-a456-556642440000",
"items": [
{ "amount" : 3000 }
]
}
This did not work because uuid in the record was null. There was no implicit String to UUID conversion. So I added the non default constructor:
public Order(String uuid, List<OrderItem> items) {
this(UUID.fromString(uuid), items);
}
But Spring does not like it:
java.lang.IllegalStateException: No primary or single unique constructor found for class com.example.fueltransport.beans.Order
at org.springframework.beans.BeanUtils.getResolvableConstructor(BeanUtils.java:267) ~[spring-beans-5.3.21.jar:5.3.21]
Am I supposed to write some unmarshaller for the record or is there some easier way?
add default constructor to your class:
class Order{
//some code and field
public Order(){
this.uuid = null;
this.items = null;
}
}
I think the solution will be fixed this exception but the problem remains because the Controller with JSON converter first creates an object and second fill the received fields for the object. but the java record has final fields and can't set the fields of the java record again.
for solving your problem you converter to support converting java records for example if you use the JACKSON converter should use the Jackson 2.12+. read this link maye be useful.
For anyone who may happen to stumble upon this, I think a better solution may look like this (assuming OrderItem class is serializable):
public record Order(UUID uuid, List<OrderItem> items) {
#ConstructorProperties({"uuid", "items"})
public Order(String uuid, List<OrderItems> items) {
this(UUID.fromString(uuid), items);
}
}
I'm having trouble converting between java.sql.Timestamp and java.time.Instant using JOOQ converters.
Here's a simplified version of the code I'm working with.
public class User {
private static final Converter<Timestamp, Instant> MY_CONVERTER= Converter.of(
Timestamp.class,
Instant.class,
t -> t == null ? null : t.toInstant(),
i -> i == null ? null : Timestamp.from(i)
)
public static Table<?> table = DSL.table("user");
public static Field<String> name = DSL.field(DSL.name(table.getName(), "name"), String.class);
public static Field<Instant> name = DSL.field(DSL.name(table.getCreated(), "created"), SQLDataType.TIMESTAMP.asConvertedDataType(Converter.of(MY_CONVERTER)));
}
private class UserDto {
private String name;
private Instant created;
// getters, setters, etc.
}
public class UserWriter {
// constructor with injected DefaultDSLContext etc..
public void create(UserDto user) {
dslContext.insertInto(User.table, User.firstName, User.lastName)
.values(user.getName(), user.getCreated())
.execute();
}
}
public class UserReader {
// constructor with injected DefaultDSLContext etc..
public Result<Record> getAll() {
return dslContext.select().from(User.table).fetch();
}
}
public class UserService {
// constructor with injected UserReader etc..
public Collection<UserDto> getAll() {
return userReader
.getAll()
.stream()
.map(Users::from)
.collect(Collectors.toList());
}
}
public class Users {
public static UserDto from(Record record) {
UserDto user = new UserDto();
user.setName(record.get(User.name));
user.setCreated(record.get(User.created);
return user;
}
}
When I create a new User the converter is called and the insertion works fine. However, when I select the Users the converter isn't called and the record.get(User.created) call in the Users::from method returns a Timestamp (and therefore fails as UserDto.setCreated expects an Instant).
Any ideas?
Thanks!
Why the converter isn't applied
From the way you phrased your question (you didn't post the exact SELECT statement that you've tried), I'm assuming you didn't pass all the column expressions explicitly. But then, how would jOOQ be able to find out what columns your table has? You declared some column expressions in some class, but that class isn't following any structure known to jOOQ. The only way to get jOOQ to fetch all known columns is to make them known to jOOQ, using code generation (see below).
You could, of course,let User extend the internal org.jooq.impl.TableImpl class and use internal API to register the Field values. But why do that manually, if you can generate this code?
Code generation
I'll repeat the main point of my previous question, which is: Please use the code generator. I've now written an entire article on why you should do this. Once jOOQ knows all of your meta data via code generation, you can just automatically select all columns like this:
UserRecord user = ctx
.selectFrom(USER)
.where(USER.ID.eq(...))
.fetchOne();
Not just that, you can also configure your data types as INSTANT using a <forcedType>, so you don't need to worry about data type conversion every time.
I cannot stress this enough, and I'm frequently surprised how many projects try to use jOOQ without code generation, which removes so much of jOOQ's power. The main reason to not use code generation is if your schema is dynamic, but since you have that User class, it obviously isn't dynamic.
Say I have a pojo class:
public class event {
String eventId;
String date;
String state;
}
And I want to save my event class in 2 separate collections in MongoDB.
I can use mongoTemplate for this:
mongoTemplate.save(event, "collection_1");
mongoTemplate.save(event, "collection_2");
But I run into problems because the collections need to have a different way of handeling the documents.
The first collection should store every event as a new
document. But the documents should expire after x seconds.
The second collection should only store the latest entry for a given
eventId.
Using annotations I can achieve 1. and 2. separately in the following way:
Criteria 1.
#Document
public class event {
String eventId;
#Indexed(expireAfterSeconds = x)
String date;
String state;
}
Criteria 2.
#Document
public class event {
#Id
String eventId;
String date;
String state;
}
I can't find a good way of achieving both criteria at the same time.
I know I could for example create 2 more classes that have the same fields as the event class but with different annotations and have a constructor that takes an event as an argument. But this really does not feel like a good solution with all the duplicate code.
Is there a more elegant solution for this problem?
So I finally found an awnser to my question. I'll post it here incase anyone else encounters the same problem.
The trick is to set the indexing through a configuration file instead of using annotations.
#Configuration
#DependsOn("mongoTemplate")
public class MongoCollectionConfiguration {
private final MongoTemplate mongoTemplate;
#Autowired
public MongoCollectionConfiguration(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
#PostConstruct
public void initIndexes() {
mongoTemplate.indexOps("collection_1")
.ensureIndex(
new Index().on("location.timestamp", Sort.Direction.DESC).expire(604800)
);
mongoTemplate.indexOps("collection_2")
.ensureIndex(
new Index().on("location.timestamp", Sort.Direction.DESC).unique()
);
}
I hope this helps someone in the future, and ofcourse I am open to any improvement!
I am in a situation where I have to store data belonging to multiple entities in a single collection. But when I query then back, I dont want unwanted records in my result. How can we achieve this using spring? Below is what I have done so far.
1. I give same collection name in entity as shown below.
#Document(collection = "livingThings")
#Data
public class AnimalEntity {
//contains id, type, bla, bla
}
#Document(collection = "livingThings")
#Data
public class HumanEntity {
//contains id, gender, address
}
2. I create independent mongoRepository interfaces
public interface AnimalRepository implements MongoRepository<AnimalEntity, String> {
}
public interface HumanRepository implements MongoRepository<HumanEntity, String> {
}
3. And the problem is
when I do animalRepo.findAll or humanRepo.findAll, I get all records available in the collection.
4. What I expect
animalRepo.findAll returns only those records where document structure is same as AnimalEntity.
Thank you very much for your time and patience to attend this query.
MongoDB automatically adds _class field to entities in a collection. Even though it is not the best solution, you can try this:
#Query("_class:your package name here.AnimalEntity")
public AnimalEntity findAllAnimals();
I have an entity named Commercial. I have an Category entity where the list of commercial categories are hold. For each category there is an separate entity extending Commercial(like RestaurantCommercial, PhotoStudioCommercial etc. total up to 20 entities) with JOINED inheritance strategy.
Commercial entity holds up general properties like title, text contactnumber of some company's commercial, while RestaurantCommercial and PhotoStudioCommercial holds additional specific properties concerned with that category.
The problem is that writing a separate dao and controller for each entity is a bit plenty of work, so I am searching for a neat way to handle this issue.
I need an unified controller and may be the DAO for handling the form control and persisting new instances of the entities that extend Commercial.
Here is approximately what I was thinking about:
#RequestMapping(value={"/vendor/commercial/{categoryName}/new"}, method=RequestMethod.GET)
public String showNewCommercialForm(#PathVariable("categoryName") String categoryName,
Map<String, Object> map) {
Category category = categoryDAO.getCategoryByServiceName(categoryName);
Class clazz = Class.forName(category.getClassName()); //here className is smth like domain.commercial.RestaurantCommercial
map.put("commercialInstance", clazz.newInstance());
//separate view for each category of commercial
return "vendor/commercial/"+categoryName+"/new";
}
And I was thinking for a similar controller for saving form data even if I would have to write a sperate binder for this stuff.
So the question is: What would you suggest to handle this issue or what would be the best practice if you had already faced similar need(Generics, Reflection or smth else)? Or if that would be worthy or not and why?
Thanks in advance.
I create a Dao interface for such cases:
public interface Dao<T extends Commercial> {
T getAll();
}
After that an abstract Dao implementation, for example hibernate based:
public CommonHibernateDao<T extends Commercial> implements Dao<T>
{
private final Class<T> entityType;
protected CommonHibernateDao(Class<T> entityType) {
this.entityType = entityType;
}
public List<T> getAll() {
// hibernate get all implementation
}
}
And RestaurantCommercial Dao interface and implementation:
public interface RestaurantCommercialDao extends Dao<RestaurantCommercial> {
}
public class HibernateRestaurantCommercialDao extends CommonHibernateDao<RestaurantCommercial> implements RestaurantCommercialDao {
public HibernateRestaurantCommercialDao() {
super(RestaurantCommercial.class);
}
}
All implementation goto CommonHibernateDao. In it's subclasses only constructor calling neaded. Basically you can do it with reflection but as for me it is not clear.
For controller (something like RESTfull API):
#Controller
public class YourController() {
#RequestMapping(value = "/restapi/{entityType}")
public String postEntity(HttpServletRequest request, #PathVariable(value = "entityType") String entityType) {
// If enetity name will be packegae + class name
Object beanInstance = Class.forName(entityName);
ServletRequestDataBinder binder = new ServletRequestDataBinder(beanInstance);
binder.bind(request);
// Here by type of entity you can get DAO and persist
}
}
If form input names will be same to your bean names - binding will do all automatically.