I have document with "_id" : ObjectId("5449567cdf97f277c50d1ce2") but I am getting null when trying to get it by id using findOne. How to solve it?
main
public class MongoDBJDBC {
public static void main( String args[] ){
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
ProductService productService = context.getBean(ProductService.class);
Product product = productService.get("5449567cdf97f277c50d1ce2"); // null here
}
}
service
#Service
public class ProductService {
#Autowired
private ProductDao productDao;
#Autowired
private ProductPropertiesDao productPropertiesDao;
public void add(Product product) {
productDao.save(product);
}
public void update(Product product) {
productDao.save(product);
}
public Product get(String id) {
return productDao.get(id);
}
dao
#Repository
public class ProductDao {
#Autowired
private MongoOperations mongoOperations;
public Product get(String id) {
return mongoOperations.findOne(Query.query(Criteria.where("id").is(new ObjectId(id))), Product.class);
}
}
entity
#Document(collection = Product.COLLECTION_NAME)
public class Product implements Serializable {
public Product() {
}
public static final String COLLECTION_NAME = "product";
#Id
private String _id;
private String name;
}
document
{
"_id" : ObjectId("5449567cdf97f277c50d1ce2"),
"name" : "product"
}
MongoDB adds underline prefix before ID field, so you should use Criteria.where("_id"). instead of just id.
Related
I can't figure out how to handle nested JSON values in my Java classes. To make it simple as possible, I created four Java classes with every 'nested level'. However, I'm trying to have all these values in one Java class. How can I do this?
Json:
{
"_embedded":{
"events":[
{
"name":"KISS | End Of The Road World Tour",
"dates":{
"start":{
"dateTime":"2021-06-12T19:00:00Z"
},
"classifications":[
{
"name":"Rock"
}
],
"_embedded":{
"venues":[
{
"name":"Atlas Arena"
}
]
}
}
}
]
}
}
Java classes:
#Data
public class EventList {
#JsonProperty("_embedded")
private Events events;
}
#Data
public class Events {
#JsonProperty("events")
public List<EventDetails> eventsList;
}
#Data
public class EventDetails {
private String name;
#JsonProperty("dates.start.dateTime")
private String startDate;
#JsonProperty("classifications.genre.name")
private String musicType;
#JsonProperty("_embedded.venues")
private List<Venues> eventPlaceName;
}
#Data
public class Venues {
private String name;
}
You can club all the supporting classes in one class like below :
#Data
public class Start {
#JsonProperty("dateTime")
public Date dateTime;
public static class Venue {
#JsonProperty("name")
public String name;
}
#Data
public static class Classification {
#JsonProperty("name")
public String name;
}
#Data
public static class Embedded2 {
#JsonProperty("venues")
public List<Venue> venues;
}
#Data
public static class Dates {
#JsonProperty("start")
public Start start;
#JsonProperty("classifications")
public List<Classification> classifications;
#JsonProperty("_embedded")
public Embedded2 _embedded;
}
#Data
public static class Event {
#JsonProperty("name")
public String name;
#JsonProperty("dates")
public Dates dates;
}
#Data
public static class Embedded {
#JsonProperty("events")
public List<Event> events;
}
#Data
public static class Root {
#JsonProperty("_embedded")
public Embedded _embedded;
}
}
You can test (I am using Jackson for deserialization)
create ObjectMapper class and deserialize into a Root class
public class TestJson {
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
try {
Start.Root root = objectMapper.readValue(new File("C:\\Anurag\\Development\\CodeBase\\demo\\src\\main\\java\\com\\example\\demo\\domain\\testJson\\test.json"), Start.Root.class);
System.out.println(root);
} catch (Exception e) {
e.printStackTrace();
}
}
}
-When debugging, you'll notice that our objects have been filled accordingly:
**Changes done as per your requiement **
#Data
public class EventList {
#Getter
#JsonProperty("_embedded")
private Events events;
#Data
public static class Venue {
#JsonProperty("name")
public String name;
}
#Data
public static class Classification {
#JsonProperty("name")
public String name;
}
#Data
public static class Embedded2 {
#JsonProperty("venues")
public List<Venue> venues;
}
#Data
public static class Dates {
#JsonProperty("start")
public Start start;
#JsonProperty("classifications")
public List<Classification> classifications;
#JsonProperty("_embedded")
public Embedded2 _embedded;
}
#Data
public static class EventDetails {
#JsonProperty("name")
public String name;
#JsonProperty("dates")
public Dates dates;
}
#Data
public static class Events {
#JsonProperty("events")
public List<EventDetails> eventsList;
}
#Data
public static class Start {
#JsonProperty("dateTime")
public Date dateTime;
}
}
public class TestJson {
public static void main(String[] args) {
List<EventList.EventDetails> anyCity = findEventByCity("any city");
anyCity.stream().forEach(p-> {
System.out.println(p);
});
}
#SneakyThrows
static List<EventList.EventDetails> findEventByCity(String city) {
ObjectMapper objectMapper = new ObjectMapper();
EventList eventList = objectMapper.readValue(new File("C:\\Anurag\\Development\\CodeBase\\demo\\src\\main\\java\\com\\example\\demo\\domain\\testJson\\test.json"), EventList.class);
List<EventList.EventDetails> eventsList = eventList.getEvents().getEventsList();
return eventsList;
}
}
Description:
Parameter 5 of constructor in com.example.springmysqlelastic.utils.ElasticSynchronizer required a bean of type 'com.example.springmysqlelastic.mapper.FoodMapper' that could not be found.
Action:
Consider defining a bean of type 'com.example.springmysqlelastic.mapper.FoodMapper' in your configuration.
There is a MySQL to Elasticsearch sync module and all was working for User entity. Then i added new Food entity. I set files. But now im getting Bean error on FoodMapper.
Project info: food, restaurant, user search on Elasticsearch engine with Spring.
FoodMapper.java
#Mapper(componentModel = "spring")
public interface FoodMapper {
FoodDTO toFoodDTO(Food food);
List<FoodDTO> toFoodDtos(List<Food> foods);
Food toFood(FoodDTO foodDTO);
List<Food> toFoods(List<FoodDTO> foodDTOS);
FoodModel toFoodModel(Food food);
}
UserMapper.java
#Mapper(componentModel = "spring")
public interface UserMapper {
UserDTO toUserDTO(User user);
List<UserDTO> toUserDtos(List<User> users);
User toUser(UserDTO userDTO);
List<User> toUsers(List<UserDTO> userDTOS);
UserModel toUserModel(User user);
}
FoodService.java
#Service
public class FoodService implements IFoodService {
private IFoodDAO foodDAO;
private FoodMapper foodMapper;
#Autowired
public FoodService(IFoodDAO foodDAO, FoodMapper foodMapper) {
this.foodDAO = foodDAO;
this.foodMapper = foodMapper;
}
#Override
public FoodDTO save(FoodDTO foodDTO) {
Food food = this.foodDAO.save(this.foodMapper.toFood(foodDTO));
return this.foodMapper.toFoodDTO(food);
}
#Override
public FoodDTO findById(Long id) {
return this.foodMapper.toFoodDTO(this.foodDAO.findById(id).orElse(null));
}
#Override
public List<FoodDTO> findAll() {
return this.foodMapper.toFoodDtos(this.foodDAO.findAll());
}
}
ElasticSynchorizer.java
#Service
public class ElasticSynchronizer {
private IUserDAO userDAO;
private IUserESRepo userESRepo;
private UserMapper userMapper;
private IFoodDAO foodDAO;
private IFoodESRepo foodESRepo;
private FoodMapper foodMapper;
private static final Logger LOG = LoggerFactory.getLogger(ElasticSynchronizer.class);
#Autowired
public ElasticSynchronizer(IUserDAO userDAO, IUserESRepo userESRepo, UserMapper userMapper, IFoodDAO foodDAO, IFoodESRepo foodESRepo, FoodMapper foodMapper) {
this.userDAO = userDAO;
this.userESRepo = userESRepo;
this.userMapper = userMapper;
this.foodDAO = foodDAO;
this.foodESRepo = foodESRepo;
this.foodMapper = foodMapper;
}
#Scheduled(cron = "0 */3 * * * *")
#Transactional
public void sync() {
LOG.info("Start Syncing Users - {}", LocalDateTime.now());
this.syncUsers();
LOG.info(" End Syncing Users - {}", LocalDateTime.now());
LOG.info("Start Syncing Foods- {}", LocalDateTime.now());
this.syncFoods();
LOG.info(" End Syncing Foods - {}", LocalDateTime.now());
}
private void syncUsers() {
Specification<User> userSpecification = (root, criteriaQuery, criteriaBuilder) ->
getModificationDatePredicate(criteriaBuilder, root);
List<User> userList;
if (userESRepo.count() == 0) {
userList = userDAO.findAll();
} else {
userList = userDAO.findAll(userSpecification);
}
for(User user: userList) {
LOG.info("Syncing User - {}", user.getId());
userESRepo.save(this.userMapper.toUserModel(user));
}
}
private void syncFoods() {
Specification<Food> userSpecification = (root, criteriaQuery, criteriaBuilder) ->
getModificationDatePredicate(criteriaBuilder, root);
List<Food> foodList;
if (userESRepo.count() == 0) {
foodList = foodDAO.findAll();
} else {
foodList = foodDAO.findAll(userSpecification);
}
for(Food food: foodList) {
LOG.info("Syncing Food - {}", food.getId());
foodESRepo.save(this.foodMapper.toFoodModel(food));
}
}
private static Predicate getModificationDatePredicate(CriteriaBuilder cb, Root<?> root) {
Expression<Timestamp> currentTime;
currentTime = cb.currentTimestamp();
Expression<Timestamp> currentTimeMinus = cb.literal(new Timestamp(System.currentTimeMillis() -
(Constants.INTERVAL_IN_MILLISECONDE)));
return cb.between(root.<Date>get(Constants.MODIFICATION_DATE),
currentTimeMinus,
currentTime
);
}
}
Food.java
#NoArgsConstructor
#AllArgsConstructor
#Data
#Entity
public class Food {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private float price;
private String category;
private String description;
private String imglink;
private String restaurant;
#Temporal(TemporalType.TIMESTAMP)
#UpdateTimestamp
private Date modificationDate;
}
Foodmodel.java
#NoArgsConstructor
#AllArgsConstructor
#Data
#Document(indexName = "food") //type deprecate oldu hepsi artik _doc
public class FoodModel {
private Long id;
private String name;
private float price;
private String category;
private String description;
private String imglink;
private String restaurant;
private Date modificationDate;
}
FoodDTO.java
#NoArgsConstructor
#AllArgsConstructor
#Data
public class FoodDTO {
private Long id;
private String name;
private float price;
private String category;
private String description;
private String imglink;
private String restaurant;
}
IFoodDAO.java
#Repository
public interface IFoodDAO extends JpaRepository<Food, Long>, JpaSpecificationExecutor<Food> {
//List<User> findByNameContaining(String name);
}
IFoodESRepo.java
public interface IFoodESRepo extends ElasticsearchRepository<FoodModel, Long> {
//List<Food> findByNameContaining(String name);
}
main class
#SpringBootApplication
#EnableElasticsearchRepositories("com.example.springmysqlelastic.repo.elastic")
#EnableScheduling
#EnableJpaRepositories("com.example.springmysqlelastic.repo")
//#ComponentScan(basePackages = {"com.example.springmysqlelastic"})
//#EnableAutoConfiguration
public class SpringMysqlElasticApplication {
public static void main(String[] args) {
SpringApplication.run(SpringMysqlElasticApplication.class, args);
}
}
structure
enter image description here
enter image description here
I found that i can't use multiple mappers. Moved second mapper to first now works.
Don't use more than 1 mapper.
I created a chat on spring boot. Where several people can correspond. So I created a database and there I store all messages from users. So I want, if a new user enters the chat, then he should see only the last 10 messages. The problem is that the program does not take the last 10 messages from the database, it takes them from the server, this is not correct. I want him to take the last 10 messages from the database.
My code
Rest Controller
#SpringComponent
#org.springframework.web.bind.annotation.RestController
public class RestController {
private List<Message> store;
public RestController() {
store = new ArrayList<>();
}
#PutMapping("/api/save")
public void saveMessage(#RequestBody String chatMessage) {
store.add(new Gson().fromJson(chatMessage, Message.class));
if (store.size() > 10)
store.remove(0);
}
#GetMapping("/api/last")
public String getLasts() {
return new Gson().toJson(store);
}
}
Message class
#Entity
#Table(name = "chatMessages")
public class Message {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String fromV;
private String messageV;
private Timestamp time;
public Timestamp getTime() {
return time;
}
public void setTime(Timestamp time) {
this.time = time;
}
public Message() { }
public String getFromV() {
return fromV;
}
public void setFromV(String fromV) {
this.fromV = fromV;
}
public String getMessageV() {
return messageV;
}
public void setMessageV(String messageV) {
this.messageV = messageV;
}
public Message(String from, String message) {
this.fromV = from;
this.messageV = message;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFrom() {
return fromV;
}
public void setFrom(String from) {
this.fromV = from;
}
public String getMessage() {
return messageV;
}
public void setMessage(String message) {
this.messageV = message;
}
}
MessageRepository
#Repository
public interface MessageRepository extends JpaRepository<Message, Long> {
}
MessageService
public interface MessageService {
void add(Message message);
List<Message> getAllMessages();
}
MessageServiceImpl
#Service
#Transactional
public class MessageServiceImpl implements MessageService {
private final MessageRepository repository;
#Autowired
public MessageServiceImpl(MessageRepository repository) {
this.repository = repository;
}
#Override
public void add(Message message) {
message.setTime(new Timestamp(new Date().getTime()));
repository.saveAndFlush(message);
}
#Override
public List<Message> getAllMessages() {
return repository.findAll();
}
}
MessageList
public MessageList() {
addClassName("message-list");
}
#Override
public void add(Component... components) {
super.add(components);
components[components.length-1]
.getElement()
.callFunction("scrollIntoView");
}
}
Application Controller
server.port=8080
# This is a workaround for https://github.com/vaadin/spring/issues/381
spring.servlet.multipart.enabled = false
spring.datasource.url=jdbc:mysql://localhost:3306/chat?createDatabaseIfNotExist=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
To be frankly,i don't understand the purpose of the object store in your controller. If you want to query the last 10 messages.You can just implement a method in repository and invoke it in your controller.
MessageRepository
#Repository
public interface MessageRepository extends JpaRepository<Message, Long> {
List<Message> findTop10ByOrderByTimeDesc();
}
I got 2 classes A and B which both got a company. The company of A has got a little more information than the company of B (Company has an id while CNCompany doesn´t). I want to map all fields using orika. If all fields an object are null, I want the object to be null!
I tried to express this with an unit test. What has to be done to get this running?
public class A {
private Company company;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
public class B {
private CNCompany company;
public CNCompany getCompany() {
return company;
}
public void setCompany(CNCompany company) {
this.company = company;
}
}
public class Company {
private Id id;
private AccountId accountId;
public Id getId() {
return id;
}
public void setId(Id id) {
this.id = id;
}
public AccountId getAccountId() {
return accountId;
}
public void setAccountId(AccountId accountId) {
this.accountId = accountId;
}
}
public class CNCompany {
private AccountId accountId
public AccountId getAccountId() {
return accountId;
}
public void setAccountId(AccountId accountId) {
this.accountId = accountId;
}
}
public class MyMapper extends ConfigurableMapper {
#Override
protected void configure(MapperFactory factory) {
factory.classMap(A.class, B.class) //
.mapNulls(false).mapNullsInReverse(false) //
.byDefault() //
.register();
}
}
#Test
public void testMap() throws Exception {
A a = new A();
Company company = new Company();
Id id = new Id();
id.setValue("1");
company.setId(id);
a.setCompany(company);
MyMapper myMapper = new MyMapper();
B outcome = myMapper.map(a, B.class);
assertThat(outcome.getCompany(), is(nullValue()));
}
If I understand correctly you want getCompany to return null if the Company object contains only null values.
In Orika you can control conversion with a custom converter. For your example that might look something like:
public class CompanyConverter extends CustomConverter<Company, CNCompany> {
public CNCompany convert(Company source, Type<? extends CNCompany> destinationType) {
if (isNothingButNulls(source)) {
return null;
}
final CNCompany newCompany = new CNCompany();
// do your thing
return newCompany;
}
}
I've never written a CustomConverter that can return null so I'm not 100% sure this will work but it should. Note that the converter will still need to be registered. The documentation I linked shows how to register depending on what level you want the converter at.
I have some problems with executing findOne method of MongoOperations class, for now this method return null. My data structure in mongoDB is looking like this:
> db.news.find({_id:1})
{ "_id" : 1, "title" : "first title", "text" : "first text" }
> db.news.find({_id:{$type:1}})
{ "_id" : 1, "title" : "first title", "text" : "first text" }
As you can see above _id field has Double type. My Java classes is looking like this:
#Repository
public class NewsService {
#Autowired
private MongoOperations mongoOperations;
public static final String COLLECTION_NAME = "news";
//this method executes ok
public List<NewsEntity> getAllNews() {
return mongoOperations.findAll(NewsEntity.class, COLLECTION_NAME);
}
//but this method return null
public NewsEntity getNewsDetail(Long id) {
return mongoOperations.findOne(Query.query(Criteria.where("_id").is(id)), NewsEntity.class);
}
Entity class:
#Document
public class NewsEntity {
#Id
private Long id;
private String title;
private String text;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
And Spring controller:
#Controller
public class MainController {
#Autowired
private NewsService newsService;
#RequestMapping(value="/news/details/{newsId}",method = RequestMethod.GET)
public String getNewsDetails(ModelMap model, #PathVariable("newsId") Long newsId) {
//newsEnt is null here...
NewsEntity newsEnt = newsService.getNewsDetail(newsId);
model.addAttribute("newsDet", newsEnt);
return "newsdetails";
}
}
You are calling the mongoOperations instance directly and not first retrieving a collection. So much like the findAll method you have implemented you also need the form that contains the collection as an argument:
public NewsEntity getNewsDetail(Long id) {
return mongoOperations.findOne(
Query.query(Criteria.where("_id").is(id)),
NewsEntity.class,
COLLECTION_NAME
);
}
This is covered in the documentation for findOne, also see the available method signatures in the summary.