i have a serial state of order like
public enum orderStateEnum {
STATE_UNUSED("UNUSED"),
STATE_ORDERED("ORDERED"),
STATE_ASSIGNED("ASSIGNED"),
STATE_ASSIGN_EXCEPTION("ASSIGN_EXCEPTION"),
STATE_PACKED("PACKED"),
//and so on
}
and i want to use spring.statemachine(or other state machine implementation) to manage the transition like from STATE_UNUSED to STATE_ORDERED STATE_ORDERED to STATE_ASSIGNED STATE_ORDERED to STATE_ASSIGN_EXCEPTION STATE_ASSIGNED to STATE_PACKED however all the order data is stored in database,so in my case, if i have an order with STATE_ASSIGNED state, i fetch the order state from the database,but in spring.statemachine, i have to ``` StateMachine stateMachine = new StateMachine(); stateMachine.createEvent(Event_take_order);
when i new a instance of stateMachine, it's inital state is STATE_UNUSED,however i want the inital state to be the state i fetch from the database which is STATE_ASSIGNED,how can i achieve that? i've read [https://docs.spring.io/spring-statemachine/docs/1.0.0.BUILD-SNAPSHOT/reference/htmlsingle/] but i can't find any solution in it.
When you create a new StateMachine you can get StateMachineAccessor using stateMachine.getStateMachineAccessor()
StateMachineAccessor is:-
Functional interface for StateMachine to allow more programmaticaccess to underlying functionality. Functions prefixed "doWith" will expose StateMachineAccess via StateMachineFunction for better functionalaccess with jdk7. Functions prefixed "with" is better suitable for lambdas.(From Java Docs)
StateMachineAccessor has a method called doWithAllRegions where you can provide implementation of StateMachineFunction (interface) and doWithAllRegions will execute given StateMachineFunction with all recursive regions.
So, to achieve what you are trying to do the code will look like this:-
StateMachine<orderStateEnum, Events> stateMachine = smFactory.getStateMachine();
stateMachine.getStateMachineAccessor().doWithAllRegions(access -> access
.resetStateMachine(new DefaultStateMachineContext<>(STATE_ASSIGNED, null, null, null)));
I have provided the implementation of the interfaces using lambdas.
Related
Good Morning.
I'm starting to learn some mongo right now.
I'm facing this problem right now, and i'm start to think if this is the best approach to resolve this "task", or if is bettert to turn around and write another way to solve this "problem".
My goal is to iterate a simple map of values (key) and vector\array (values)
My test map will be recived by a rest layer.
{
"1":["1","2","3"]
}
now after some logic, i need to use the Dao in order to look into db.
The Key will be "realm", the value inside vector are "castle".
Every Realm have some castle and every castle have some "rules".
I need to find every rules for each avaible combination of realm-castle.
AccessLevel is a pojo labeled by #Document annotation and it will have various params, such as castle and realm (both simple int)
So the idea will be to iterate a map and write a long query for every combination of key-value.
public AccessLevel searchAccessLevel(Map<String,Integer[]> request){
Query q = new Query();
Criteria c = new Criteria();
request.forEach((k,v)-> {
for (int i: Arrays.asList(v)
) {
q.addCriteria(c.andOperator(
Criteria.where("realm").is(k),
Criteria.where("castle").is(v))
);
}
});
List<AccessLevel> response=db.find(q,AccessLevel.class);
for (AccessLevel x: response
) {
System.out.println(x.toString());
}
As you can see i'm facing an error concerning $and.
Due to limitations of the org.bson.Document, you can't add a second '$and' expression specified as [...]
it seems mongo can't handle various $and, something i'm pretty used to abuse over sql
select * from a where id =1 and id=2 and id=3 and id=4
(not the best, sincei can use IN(), but sql allow me)
So, the point is: mongo can actualy work in this way and i need to dig more into the problem, or i need to do another approach, like using criterion.in(), and make N interrogation via mongotemplate one for every key in my Map?
I have a domain object in Spring which I am saving using JpaRepository.save method and using Sequence generator from Postgres to generate id automatically.
#SequenceGenerator(initialValue = 1, name = "device_metric_gen", sequenceName = "device_metric_seq")
public class DeviceMetric extends BaseTimeModel {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "device_metric_gen")
#Column(nullable = false, updatable = false)
private Long id;
///// extra fields
My use-case requires to do an upsert instead of normal save operation (which I am aware will update if the id is present). I want to update an existing row if a combination of three columns (assume a composite unique) is present or else create a new row.
This is something similar to this:
INSERT INTO customers (name, email)
VALUES
(
'Microsoft',
'hotline#microsoft.com'
)
ON CONFLICT (name)
DO
UPDATE
SET email = EXCLUDED.email || ';' || customers.email;
One way of achieving the same in Spring-data that I can think of is:
Write a custom save operation in the service layer that
Does a get for the three-column and if a row is present
Set the same id in current object and do a repository.save
If no row present, do a normal repository.save
Problem with the above approach is that every insert now does a select and then save which makes two database calls whereas the same can be achieved by postgres insert on conflict feature with just one db call.
Any pointers on how to implement this in Spring Data?
One way is to write a native query insert into values (all fields here). The object in question has around 25 fields so I am looking for an another better way to achieve the same.
As #JBNizet mentioned, you answered your own question by suggesting reading for the data and then updating if found and inserting otherwise. Here's how you could do it using spring data and Optional.
Define a findByField1AndField2AndField3 method on your DeviceMetricRepository.
public interface DeviceMetricRepository extends JpaRepository<DeviceMetric, UUID> {
Optional<DeviceMetric> findByField1AndField2AndField3(String field1, String field2, String field3);
}
Use the repository in a service method.
#RequiredArgsConstructor
public class DeviceMetricService {
private final DeviceMetricRepository repo;
DeviceMetric save(String email, String phoneNumber) {
DeviceMetric deviceMetric = repo.findByField1AndField2AndField3("field1", "field", "field3")
.orElse(new DeviceMetric()); // create new object in a way that makes sense for you
deviceMetric.setEmail(email);
deviceMetric.setPhoneNumber(phoneNumber);
return repo.save(deviceMetric);
}
}
A word of advice on observability:
You mentioned that this is a high throughput use case in your system. Regardless of the approach taken, consider instrumenting timers around this save. This way you can measure the initial performance against any tunings you make in an objective way. Look at this an experiment and be prepared to pivot to other solutions as needed. If you are always reading these three columns together, ensure they are indexed. With these things in place, you may find that reading to determine update/insert is acceptable.
I would recommend using a named query to fetch a row based on your candidate keys. If a row is present, update it, otherwise create a new row. Both of these operations can be done using the save method.
#NamedQuery(name="getCustomerByNameAndEmail", query="select a from Customers a where a.name = :name and a.email = :email");
You can also use the #UniqueColumns() annotation on the entity to make sure that these columns always maintain uniqueness when grouped together.
Optional<Customers> customer = customerRepo.getCustomersByNameAndEmail(name, email);
Implement the above method in your repository. All it will do it call the query and pass the name and email as parameters. Make sure to return an Optional.empty() if there is no row present.
Customers c;
if (customer.isPresent()) {
c = customer.get();
c.setEmail("newemail#gmail.com");
c.setPhone("9420420420");
customerRepo.save(c);
} else {
c = new Customer(0, "name", "email", "5451515478");
customerRepo.save(c);
}
Pass the ID as 0 and JPA will insert a new row with the ID generated according to the sequence generator.
Although I never recommend using a number as an ID, if possible use a randomly generated UUID for the primary key, it will qurantee uniqueness and avoid any unexpected behaviour that may come with sequence generators.
With spring JPA it's pretty simple to implement this with clean java code.
Using Spring Data JPA's method T getOne(ID id), you're not querying the DB itself but you are using a reference to the DB object (proxy). Therefore when updating/saving the entity you are performing a one time operation.
To be able to modify the object Spring provides the #Transactional annotation which is a method level annotation that declares that the method starts a transaction and closes it only when the method itself ends its runtime.
You'd have to:
Start a jpa transaction
get the Db reference through getOne
modify the DB reference
save it on the database
close the transaction
Not having much visibility of your actual code I'm gonna abstract it as much as possible:
#Transactional
public void saveOrUpdate(DeviceMetric metric) {
DeviceMetric deviceMetric = metricRepository.getOne(metric.getId());
//modify it
deviceMetric.setName("Hello World!");
metricRepository.save(metric);
}
The tricky part is to not think the getOne as a SELECT from the DB. The database never gets called until the 'save' method.
I am trying to get live updates on my redis ordered list without success.
It seems like it fetches all the items and just ends on the last item.
I would like the client to keep get updates upon a new order in my ordered list.
What am I missing?
This is my code:
#RestController
class LiveOrderController {
#Autowired
lateinit var redisOperations: ReactiveRedisOperations<String, LiveOrder>
#GetMapping(produces = [MediaType.TEXT_EVENT_STREAM_VALUE], value = "/orders")
fun getLiveOrders(): Flux<LiveOrder> {
val zops = redisOperations?.opsForZSet()
return zops?.rangeByScore("orders", Range.unbounded())
}
}
There is no such feature in Redis. First, reactive retrieval of a sorted set is just getting a snapshot, but your calls are going in a reactive fashion. So you need a subscription instead.
If you opt in for keyspace notifications like this (K - enable keyspace notifications, z - include zset commands) :
config set notify-keyspace-events Kz
And subscribe to them in your service like this:
ReactiveRedisMessageListenerContainer reactiveRedisMessages;
// ...
reactiveRedisMessages.receive(new PatternTopic("__keyspace#0__:orders"))
.map(m -> {
System.out.println(m);
return m;
})
<further processing>
You would see messages like this: PatternMessage{channel=__keyspace#0__:orders, pattern=__keyspace#0__:orders, message=zadd}. It will notify you that something has been added. And you can react on this somehow - get the full set again, or only some part (head/tail). You might even remember the previous set, get the new one and send the diff.
But what I would really suggest is rearchitecting the flow in some way to use Redis Pub/Sub functionality directly. For example: publisher service instead of directly calling zadd will call eval, which will issue 2 commands: zadd orders 1 x and publish orders "1:x" (any custom message you want, maybe JSON).
Then in your code you will subscribe to your custom topic like this:
return reactiveRedisMessages.receive(new PatternTopic("orders"))
.map(LiveOrder::fromNotification);
Is there a way I can construct a query that will return not just existing entities on the model/table, but also create new variables populated according to a function/query. All done in a single query.
So something like this pseudocode.
Job::whereDate('created_at', '>=', $pdate)
->with('address')
->with('Employees'->count() as newprop);
Basically I want to get the job, address and also create a new property on the returned objects in the collection that will hold the employee count for each job.
What is the best way of going about this ? Maybe I should just create a new function/property on the Job model that returns Employees count ? Would that mean the application will make more db queries or will such property be populated anytime I use eloquent to get any Job object ?
If you want to add it to all Job model instances:
public function getNewpropAttribute($value) {
return count($employees);
}
That way you can call Job::find($id)->newprop; and it will execute that function. it is described here https://laravel.com/docs/master/eloquent-mutators#accessors-and-mutators
In the spring data documentation, it says:
CassandraTemplate is the place to look for accessing functionality such as incrementing counters or ad-hoc CRUD operations.
I am trying to update a counter using the CassandraTemplate in spring-data-cassandra, but the documentation on this subject is very sparse. Is there a good example of this?
I haven't found any good example neither, but from the source of CassandraTemplate/CqlTempleate, code should look like:
Update updateOp = QueryBuilder.update(cassandraOperations.getTableName(MyMapped.class).toCql());
updateOp.with(QueryBuilder.incr("my_count_column"))
.where(QueryBuilder.eq(...) ); // Primary key clause
cassandraOperations.execute(updateOp);
You can also use custom #Query to update the counter value.
For example, supposing you have some repository:
public interface SomeRepository extends CassandraRepository<SomeEntity> {
#Query("update some_table SET counter=counter+1 WHERE value1 = ?0 AND value2= ?1;")
Object updateCounterValue(String value1, String value2);}
Of course you can also parametrize the value being updated.