Multiple sorting for file generated from beanshell scripting - sorting

I am generating the .txt file from beanshell scripting. Now I am having problem for sorting. If anyone can give any idea that will be great help. The file looks like:
UserId FirstName LastName Status roleId
2025 A B Active 3
2021 C D InActive 2
2036 E F Active 1
3012 G H Active 2
5012 I J InActive 1
Sorting should be done by while writing file by Status in ascending order, then by roleId in ascending order, then by UserId in ascending order.

Assuming
public class User {
private Integer userId;
private String firstName, lastName;
public enum Status{
ACTIVE, INACTIVE;
}
private Status status;
private Integer roleId;
// getter and setter
}
You can use a comparator like this:
public class UserComparator implements Comparator<User> {
#Override
public int compare(User o1, User o2) {
#Override
public int compare(User o1, User o2) {
int bystatus = o1.getStatus().compareTo(o2.getStatus());
if (bystatus != 0) {
return bystatus;
}
int byid = o1.getUserId().compareTo(o2.getUserId());
if (byid != 0) {
return byid;
}
return o1.getRoleId().compareTo(o2.getRoleId());
}
}
Finally:
Collections.sort(list, comparator);

Related

Why might Room database IDs all show as 0 in a log/Toast message?

I'm trying to get the id of the last inserted object into a database using Room with Android. I can fetch the last object using an SQL query and can call other methods to get the various properties of that object which the user has set when saving the object. But getId() always returns 0. When I examine the table contents in Android Studio's app inspector, I can clearly see that Room is generating a unique primary key for each row, but I just can't get at it. Can anyone suggest what the problem might be?
Here's the Dao query:
#Query("SELECT * FROM gamebooks_table WHERE gamebookId=gamebookId ORDER BY gamebookId DESC LIMIT 1")
LiveData<Gamebook> getSingleGamebookByID();
And here's the annotated entity class:
#Entity(tableName = "gamebooks_table")
public class Gamebook {
#PrimaryKey(autoGenerate = true)
private long gamebookId;
private String gamebookName;
private String gamebookComment;
private String gamebookPublisher;
private float gamebookStarRating;
public Gamebook(String gamebookName, String gamebookComment, String gamebookPublisher, float gamebookStarRating) {
this.gamebookName = gamebookName;
this.gamebookComment = gamebookComment;
this.gamebookPublisher = gamebookPublisher;
this.gamebookStarRating = gamebookStarRating;
}
public long getGamebookId() {
return gamebookId;
}
public String getGamebookName() {
return gamebookName;
}
public String getGamebookComment() {
return gamebookComment;
}
public String getGamebookPublisher() {
return gamebookPublisher;
}
public float getGamebookStarRating(){
return gamebookStarRating;
}
public void setGamebookId(long gamebookId) {
this.gamebookId = gamebookId;
}
}
SOLVED
Finally sorted this by adding an Observer to my DAO method which returns a single gamebook. Within the Observer's onChanged() method, I can loop through all Gamebooks in the LiveData List (even though there's only one because I'm limiting it to one in the SQL query) and call getId() to get their respective IDs.
mainViewModel.getSingleGamebook().observe(this, new Observer<List<Gamebook>>() {
#Override
public void onChanged(List<Gamebook> gamebooks) {
int i=0;
for(Gamebook gamebook : gamebooks){
gamebookId= gamebook.getGamebookId();
Log.d(TAG, "Gamebook Name: "+gamebook.getGamebookName()+ " Database ID: " +gamebookId);
i++;
}
}
});
I believe that your issue is due to the only constructor being available not setting the id so the LiveData uses the default value of 0 for a long.
I'd suggest having a default constructor and thus all setters/getters and (optionally) using #Ignore annotation for one of the constructors..
without #Ignore you get warnings Gamebook.java:8: warning: There are multiple good constructors and Room will pick the no-arg constructor. You can use the #Ignore annotation to eliminate unwanted constructors. public class Gamebook {
e.g. :-
#Entity(tableName = "gamebooks_table")
public class Gamebook {
#PrimaryKey(autoGenerate = true)
private long gamebookId;
private String gamebookName;
private String gamebookComment;
private String gamebookPublisher;
private float gamebookStarRating;
public Gamebook(){} /*<<<<< ADDED */
#Ignore /*<<<<< ADDED - is not required - could be on the default constructor but not both*/
public Gamebook(String gamebookName, String gamebookComment, String gamebookPublisher, float gamebookStarRating) {
this.gamebookName = gamebookName;
this.gamebookComment = gamebookComment;
this.gamebookPublisher = gamebookPublisher;
this.gamebookStarRating = gamebookStarRating;
}
public long getGamebookId() {
return gamebookId;
}
public String getGamebookName() {
return gamebookName;
}
public String getGamebookComment() {
return gamebookComment;
}
public String getGamebookPublisher() {
return gamebookPublisher;
}
public float getGamebookStarRating(){
return gamebookStarRating;
}
public void setGamebookId(long gamebookId) {
this.gamebookId = gamebookId;
}
/* ADDED setters */
public void setGamebookName(String gamebookName) {
this.gamebookName = gamebookName;
}
public void setGamebookComment(String gamebookComment) {
this.gamebookComment = gamebookComment;
}
public void setGamebookPublisher(String gamebookPublisher) {
this.gamebookPublisher = gamebookPublisher;
}
public void setGamebookStarRating(float gamebookStarRating) {
this.gamebookStarRating = gamebookStarRating;
}
}
You also probably want to be able to pass the respective id to the getSingleGamebookByID, so you may wish to change this to:-
#Query("SELECT * FROM gamebooks_table WHERE gamebookId=:gamebookId /*<<<<< ADDED to use id passed */ ORDER BY gamebookId DESC LIMIT 1")
LiveData<Gamebook> getSingleGamebookByID(long gamebookId /*<<<<< ADDED to use id passed */);
you would probably want to remove the comments.
Note the LiveData aspect has not been tested and is conjecture.
Example
This example shows that room is fine with your original code but that the issues is on the LiveData/Viewmodel side :-
public class MainActivity extends AppCompatActivity {
TheDatabase db;
GamebookDao dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* Note The Database has .allowMainThreadQueries */
db = TheDatabase.getInstance(this);
dao = db.getGamebookDao();
long gb1id = dao.insert(new Gamebook("Gamebook1","blah","Gamebook1 Publisher", 10.1F));
long gb2id = dao.insert(new Gamebook("Gamebook2","blah","Gamebook2 Publisher", 6.1F));
long gb3id = dao.insert(new Gamebook("Gamebook3","blah","Gamebook3 Publisher", 10.1F));
logGameBook(dao.getSingleGamebookByID());
logGameBook(dao.getSingleGamebookByID());
logGameBook(dao.getSingleGamebookByID());
/* Alternative that allows the ID to be specified */
logGameBook(dao.getSingleGamebookByIDAlternative(gb1id));
logGameBook(dao.getSingleGamebookByIDAlternative(gb2id));
logGameBook(dao.getSingleGamebookByIDAlternative(gb3id));
}
void logGameBook(Gamebook gb) {
Log.d("GAMEBOOKINFO","Gamebook is " + gb.getGamebookName() + " id is " + gb.getGamebookId());
}
}
The above uses your original code, the TheDatabase is a basic #Database annotated class BUT with .allowMainThreadQueries so it is run on the main thread.
The log, after running, includes:-
2022-03-12 08:16:12.556 D/GAMEBOOKINFO: Gamebook is Gamebook3 id is 3
2022-03-12 08:16:12.558 I/chatty: uid=10132(a.a.so71429144javaroomidreturnedaszero) identical 1 line
2022-03-12 08:16:12.561 D/GAMEBOOKINFO: Gamebook is Gamebook3 id is 3
2022-03-12 08:16:12.568 D/GAMEBOOKINFO: Gamebook is Gamebook1 id is 1
2022-03-12 08:16:12.572 D/GAMEBOOKINFO: Gamebook is Gamebook2 id is 2
2022-03-12 08:16:12.574 D/GAMEBOOKINFO: Gamebook is Gamebook3 id is 3
Note how the first just returns the same object and thus id.

How could I fix this error: Cannot create TypedQuery for query with more than one return using requested result type?

I have the following class:
public class House {
#Id
#GeneratedValue
private long id;
private String title;
private String description;
private String city;
private double price;
private String phoneNumber;
}
I need to make a #GetMapping, where I have to get every attribute of a house except it's city.
I tried this:
This is in my repository:
public House findByIdButCity(long id) {
return em.createQuery("SELECT h.title, h.description, h.price FROM House h WHERE h.id = :id", House.class).setParameter("id", id).getSingleResult();
}
And this is in my controller:
#GetMapping("{id}")
public ResponseEntity<House> getAttributesButCityById(#PathVariable long id) {
House house = houseRepository.findByIdButCity(id);
if (house == null)
return ResponseEntity.notFound().build();
else
return ResponseEntity.ok(property);
}
Instead of filter the fields in the select, get all the information of a house and then map it to an object in the controller with only the information that you want to return, this way you also will decouple your database model from your API:
public House findByIdButCity(long id) {
return em.createQuery("SELECT h FROM House h WHERE h.id = :id", House.class).setParameter("id", id).getSingleResult();
}
public class HoouseDto {
private String title;
private String description;
private double price;
}
#GetMapping("{id}")
public ResponseEntity<HouseDto> getAttributesButCityById(#PathVariable long id) {
House house = houseRepository.findByIdButCity(id);
if (house == null)
return ResponseEntity.notFound().build();
else {
HouseDto houseDto = mapper.toDto(house)
return ResponseEntity.ok(houseDto);
}
}
}
You can use mapstruct to make the mapper or you can create a class with a static method to do it
Mapper example
public class HouseMapper {
public static HouseDto toDto(House house) {
HouseDto houseDto = new HouseDto();
houseDto.setTitle(house.getTitle());
houseDto.setDescription(house.getDescription());
houseDto.setPrice(house.getDescription());
return houseDto;
}
You can do more easily if you include the mapstruct with spring because you only have to define an interface and it will create the implementation mapping the attributes that with same names.
Here is an example:
https://www.baeldung.com/mapstruct
try changing your query to
SELECT h FROM House h WHERE h.id = :id

getting an error in unboxing in c# stating name does not exist in current context?

class Program
{
static void Main(string[] args)
{
Console.WriteLine("enter id");
int id = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("enter name");
string name = Console.ReadLine();
Console.WriteLine("price");
int price = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("enter quantity");
int quantity = Convert.ToInt32(Console.ReadLine());
product_demo p1 = new product_demo(id, name, price, quantity);
p1.diplay();
Console.Read();
}
}
public class product_demo
{
public int id;
public string name;
public int price;
public int quantity;
public product_demo(int id, string name, int price, int quantity)
{
this.id = id;
this.name = name;
this.price = price;
this.quantity = quantity;
object o1 = id;
object o2 = name;
object o3 = price;
object o4 = quantity;
}
public void diplay()
{
int j = (int)o1;
Console.WriteLine("id :");
}
}
getting an error while unboxing in display function. boxing is done in product_demo constructor. one more question ; can we define boxing outside any constructor or methods, directly in body of class.
You have created object o1 in the constructor of product_demo and therefore it exists only within that scope. You need to have your objects within the class (Similar to where you have placed your other variables such as int id, string name, etc.

how do I solve the problem of bind enum in spring controller

when I use common param to do some common logic by using spring. I found I can`t use enum for input. like postman or other.
To solve this problem,I try lots of ways. finally,thanks god.I success.
This is only for RequestMethod.POST.And your param must be object (maybe RequestMethod.GET or single param also available.but I haven`t found how to do that )
example
#Data
#Builder
#AllArgsConstructor
#RequiredArgsConstructor
public class CommonParam implements Serializable {
/**
*
*/
#Size(min = 1, max = CommonConstants.MAX_PARTITION_SIZE)
private List<String> texts;
/**
*
*/
#NotNull
private KeyTypeEnum keyTypeEnum;
}
#PostMapping("/do")
public RpcResult do(#RequestBody #Valid CommonParam commonParam) {
.....
}
the last but not the least
public enum KeyTypeEnum {
/**
* 手机号
*/
PHONE(1, "phone");
private int value;
private String desc;
KeyTypeEnum(int value, String desc) {
this.value = value;
this.desc = desc;
}
public static KeyTypeEnum getByDesc(String desc) {
for (KeyTypeEnum b : KeyTypeEnum.values()) {
if (b.getDesc()
.equals(desc)) {
return b;
}
}
return null;
}
#JsonCreator
public static KeyTypeEnum getByValue(int value) {
for (KeyTypeEnum b : KeyTypeEnum.values()) {
if (Objects.equals(b.getValue(), value)) {
return b;
}
}
return null;
}
#JsonValue
public int getValue() {
return value;
}
public String getDesc() {
return desc;
}
}
By Using #JsonCreator we can ensure the input like '1'(this code is the KeyTypeEnum`s already defined value) can be success convert to enum.
#JsonValue ensure '1' can be success get for spring.
The value of KeyTypeEnum should be PHONE in the JSON payload. It's not feasible to uniquely resolve by the value 1 as you are expecting. You can have multiple enum with the same value.

Wrap collections in Value Objects

I have the following entities:
public class ComplexEntity {
public List<TenderLocation> tenderList;
public ComplexEntity(List<TenderLocation> tenderList) {
this.tenderList = tenderList;
}
}
public class TenderLocation {
public String location;
public List<TenderAirline> tenderAirlines;
public TenderLocation(String location, List<TenderAirline> tenderAirlines) {
this.tenderAirlines = tenderAirlines;
this.location = location;
}
}
public class TenderAirline {
public int ID;
public String name;
public TenderAirline(int ID, String name) {
this.ID = ID;
this.name = name;
}
}
And the following test for comparing two ComplexEntiey:
public class ComplexObjectGraphComparisonExample {
#Test
public void shouldCompareTwoComplexObjects() {
// given
Javers javers = JaversBuilder.javers().build();
// Construct test data
// ComplexEntity:
// - List<TLocation>
// TLoation:
// - location: String
// - List<TAir>
// TAir:
// - int ID
// - String Name
int locations = 3;
List<TenderLocation> tenderLocationsBase = new ArrayList<TenderLocation>(locations);
List<TenderLocation> tenderLocationsRef = new ArrayList<TenderLocation>(locations);
for (int j = 0; j < locations; ++j) {
int airlines = 10;
List<TenderAirline> tenderAirlinesBase = new ArrayList<TenderAirline>(airlines);
List<TenderAirline> tenderAirlinesRef = new ArrayList<TenderAirline>(airlines);
for (int i = 0; i < airlines; ++i) {
tenderAirlinesBase.add(new TenderAirline(i, "Airline" + i));
tenderAirlinesRef.add(new TenderAirline(i, "Airline" + i));
}
tenderLocationsBase.add(new TenderLocation("BV" + j, tenderAirlinesBase));
tenderLocationsRef.add(new TenderLocation("BV" + j, tenderAirlinesBase));
}
ComplexEntity baseEntity = new ComplexEntity(tenderLocationsBase);
ComplexEntity referenceEntity = new ComplexEntity(tenderLocationsRef);
// when
Diff diff = javers.compare(baseEntity, referenceEntity);
assertThat(diff.getChanges()).hasSize(0);
// Change a single small thing
referenceEntity.tenderList.get(1).location = "Difference_1";
// then there is a single change detected
diff = javers.compare(baseEntity, referenceEntity);
assertThat(diff.getChanges()).hasSize(1);
// there should be one change of type {#link ValueChange}
ValueChange change = diff.getChangesByType(ValueChange.class).get(0);
assertThat(change.getPropertyName()).isEqualTo("location");
assertThat(change.getLeft()).isEqualTo("BV1");
assertThat(change.getRight()).isEqualTo("Difference_1");
// do another change
referenceEntity.tenderList.get(1).tenderAirlines.get(1).name = "Difference_2";
// second difference is not detected, failing the commented test
diff = javers.compare(baseEntity, referenceEntity);
assertThat(diff.getChanges()).hasSize(2);
System.out.println(diff);
}
}
At comparison my second change is not identified because the compare method is not comparing in depth my lists.
I have read here
http://www.atetric.com/atetric/javadoc/org.javers/javers-core/1.3.4/org/javers/core/Javers.html
that if I "wrap collections in some Value Objects" the deep comparing of the collection is possible.
My question is, How exactly I can wrap my collection into Value Objects?
You can wrap the object something like below:
public class Wrapper
{
private final WrappedObject obj;
public Wrapper (WrappedObject obj)
{
this.obj = obj;
}
}
What is wrong in you code is mapping, you didn't do it at all. You should map your entities as Entities using #Id annotation:
public class TenderLocation {
#Id
public String location;
...
public class TenderAirline {
#Id
public int ID;
public String name;
...
Otherwise, JaVers maps your classes as Value Objects (objects without identity) which gives you limited diff experience.

Resources