I am trying to implement an object-oriented design for stack overflow. I have Member, Question, Answer, Comment object. (not considering tags, bounty other objects for simplicity). I am not able to figure out the storage of data in design.
The user creates a question, Answer, Comment with a title and description. I am not able to figure out where I should save the data because these objects belong to the user and global search also. Answer and comment can also happen on any question but they belong to the user also. so the problem is to store the data user-specific and global.
I have a thought process to create a repository that will keep data for questions, answers, and comments. I can store questionsIds, answerIds, commentIds in member object. similarly, anserIds and comments in Question object. I am not sure it's good practice for object-oriented design or not.
public class Member {
private long memberId;
// few more properties
Member(){
}
public long createQuestion(String title, String description){ return 1;}
public long createAnswer(long questionId, String description){ return 1;}
public long createQuestionComment(long questionId, String description){ return 1;}
public long createAnswerComment(long answerId, String description){ return 1;}
}
public class Question {
private long id;
private String title;
private String description;
Question(String title, String description){
}
}
In case, you want more details. I will create a UML dig for more explanation. If someone knows about any good article please refer me there. If you know about any good book where I can learn more about the object-oriented approach please let me know that also.
The storage is a separate issue from how you interact with it.
In a classic SQL setup the member, answer, comment, etc are all separate database tables with numeric id keys. The relationships are likely one-directional where the comment has a memberId field, but the member would not know anything about the comments. You would find a member's comments by querying the comment table for comments with the correct memberId. Here's your StackOverflow comments, which we find through the query SELECT * FROM Comments WHERE UserId = 8906489.
These classes that you are designing would be used to interact with those database tables and create a local (per page load) cache system. In terms of the Model-View-Controller pattern the database tables are the Model and these classes are the Controller. Your Member class knows its memberId so it can create and execute a comments query that selects comments for that id, or that inserts a comment with that member id, etc.
You don't want to have the same data in lots of places so you probably have some sort of central Repository class. When you are calling createQuestion on the Member that needs to be linked to the Repository. You would either design it so that instances of Member get the Repository as a constructor argument, or so that all interactions are initiated by the Repository, which can pass itself as a function argument.
I have created an OO solution with a repository structure. I have considered simple functionality only. I think we can break the repository into separate repositories like AnswerRepository, QuestionRepository.
Main:
public class StackOverflow {
public static void main(String[] args) {
StackOverflow app= new StackOverflow();
StackOverflowRepository repository = new StackOverflowRepository();
String memberName = "Vimit"; // assume this as a input from application
long memberId = app.registerMember(memberName, repository);
long questionId = app.createQuestion(repository,memberId, "OO design stackoverflow", "How to do OO design for stack overflow questions"); // when anymmeber will call we have only member id
app.createAnswer(repository, "I dont know about OO design", questionId, memberId); // whe someone call this function, it will be meber and on some squestion so we always have member Id
app.searchQuestions( "ood", repository);
}
public long registerMember(String memberName, StackOverflowRepository repository){
Member member = new Member(memberName);
repository.addMember(member);
return member.getId();
}
public long createQuestion(StackOverflowRepository repository, long memberId, String title, String description){
Member member = repository.getMember(memberId);
return member.createQuestion(repository, title, description);
}
public long createAnswer(StackOverflowRepository repository, String description, long questionId, long memberId){
Member member = repository.getMember(memberId);
return member.createAnswer(repository, description,questionId);
}
public List<Question> searchQuestions(String searchText, StackOverflowRepository repository){
return repository.searchQuestion(searchText);
}
}
Repository:
public class StackOverflowRepository {
private AtomicLong memberId;
private AtomicLong questionId;
private AtomicLong answerId;
private Map<Long, Member> members;
private Map<Long, Question> questions;
private Map<Long, Answer> answers;
private Map<String, List<Long>> searchQuestions; // reverseIndex for question search
StackOverflowRepository() {
memberId = new AtomicLong(0);
questionId = new AtomicLong(0);
members = new HashMap<>();
questions = new HashMap<>();
}
public Question addQuestion(long memberId, Question question){
question.setId(questionId.incrementAndGet());
questions.put(questionId.get(), question);
members.get(memberId).addQuestionId(questionId.get());
createReverseIndex(question);
return question;
}
public long addMember(Member member) {
member.setId(memberId.incrementAndGet());
members.put(memberId.get(), member);
return memberId.get();
}
public Member getMember(long memberId){
return members.get(memberId);
}
public List<Question> searchQuestion(String search){
String[] words = search.split("//s");
List<Long> questionIds = new ArrayList<>();
for(String word: words){
questionIds.addAll(searchQuestions.get(word));
}
return questionIds.stream().distinct().map(id -> questions.get(id)).collect(Collectors.toList());
}
public long addAnswer(Answer answer, long memberId, long questionId){
answer.setId(answerId.incrementAndGet());
answers.put(answerId.get(), answer);
members.get(memberId).addAnswerId(answerId.get());
questions.get(memberId).addAnswerId(answerId.get());
return answer.getId();
}
private void createReverseIndex(Question question){
String[] words = question.getDescription().split("//s");
for(String word: words){
List<Long> ids = searchQuestions.getOrDefault(word, new ArrayList<>());
ids.add(question.getId());
searchQuestions.put(word, ids);
}
}
}
Member:
public class Member {
private long id;
private String memberName;
private List<Long> questionIds;
private List<Long> answerIds;
Member(String memberName){
this.memberName = memberName;
}
public long createQuestion(StackOverflowRepository stackOverflowRepository, String title, String description){
Question question = new Question(title, description);
stackOverflowRepository.addQuestion(this.id,question);
return question.getId();
}
public long createAnswer(StackOverflowRepository stackOverflowRepository, String description, long questionId){
Answer answer = new Answer(description);
return stackOverflowRepository.addAnswer(answer, this.id, questionId);
}
public void addQuestionId(long id){
this.questionIds.add(id);
}
public void setId(long id){
this.id = id;
}
public long getId(){
return id;
}
public void addAnswerId(long id) {
answerIds.add(id);
}
}
Question:
public class Question {
private Long id;
private String title;
private String description;
private List<Long> answerIds;
Question(String title, String description){
this.title = title;
this.description = description;
}
public void setId(Long id){
this.id = id;
}
public long getId(){
return this.id;
}
public void addAnswerId(long id) {
this.answerIds.add(id);
}
public String getDescription() {
return this.description;
}
}
Answer:
public class Answer {
private Long id;
private String description;
Answer(String description) {
this.description = description;
}
public void setId(Long id){
this.id = id;
}
public long getId(){
return this.id;
}
}
Related
I have the following table
CREATE TABLE magazines.magazine_name (
frequency smallint,
magazine_id varchar,
magazine_name varchar,
PRIMARY KEY (magazine_id,magazine_name)
);
Should I use allow filter annotation to have the following repository method get executed
#Query("SELECT * from magazine_name where magazine_id = ?0")
MagazineName findMagazineCQlQuery(String id);
because I get the folowing execption :
org.springframework.data.cassandra.CassandraInvalidQueryException:Query;
CQL[com.datastax.oss.driver.internal.core.cql.DefaultSimpleStatement#c78c2039];
Cannot execute this query as it might involve data filtering and thus may have unpredictable performance.
If you want to execute this query despite the performance unpredictability, use ALLOW FILTERING;
nested exception is
com.datastax.oss.driver.api.core.servererrors.InvalidQueryException:
Cannot execute this query as it might involve data filtering
and thus may have unpredictable performance.
If you want to execute this query despite the performance unpredictability, use ALLOW FILTERING
By the way, I know that I can use query methods or even findById method, but actually I am just experimenting with cql quires and try to learn about it.
--update
The domain object
#Table(value = "magazine_name")
#Data
#Builder
public class MagazineName {
#PrimaryKeyColumn(name = "magazine_id", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
private String magazineId;
#PrimaryKeyColumn(name = "magazine_name", ordinal = 1, type = PrimaryKeyType.CLUSTERED)
private String name;
}
I defined the table exactly like yours and here is my repository. I can query without error.
1. My Repository
public interface IMagazineDao extends CrudRepository<Magazine, String> {
#Query("SELECT * from magazine_name where magazine_id = ?0")
Magazine findMagazineCQlQuery(String id);
}
2. Application
#SpringBootApplication
public class Application implements CommandLineRunner {
#Autowired
private IMagazineDao magazineDao;
#Override
public void run(String... args) throws Exception {
this.magazineDao.save(new Magazine("magazine1", "name", (short) 1));
this.magazineDao.findMagazineCQlQuery("magazine1");
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3. Magazine class
#Table(value = "magazine_name")
public class Magazine {
#PrimaryKeyColumn(name = "magazine_id", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
private String magazineId;
#PrimaryKeyColumn(name = "magazine_name", ordinal = 1, type = PrimaryKeyType.CLUSTERED)
private String name;
#Column
private Short frequency;
public Magazine() {
}
public Magazine(String magazineId, String name, Short frequency) {
this.magazineId = magazineId;
this.name = name;
this.frequency = frequency;
}
public String getMagazineId() {
return magazineId;
}
public void setMagazineId(String magazineId) {
this.magazineId = magazineId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Short getFrequency() {
return frequency;
}
public void setFrequency(Short frequency) {
this.frequency = frequency;
}
}
What are your methods to deal with the communication of an admin panel with a domain in the case of changing values of properties of an entity without breaking the encapsulation?
public class Book : Entity {
public Book(string title, string author, string description, decimal price, short publicationYear) {
Title = title;
Author = author;
Description = description;
Price = price;
PublicationYear = publicationYear;
}
public string Title { get; private set; }
public string Author { get; private set; }
public string Description { get; private set; }
public decimal Price { get; private set; }
public short PublicationYear { get; private set; }
}
The only way to not break encapsulation is to include some parts of the presentation logic into the object itself. Not the details, mind you, but the parts which are highly coupled to this object.
I would do something like this (pseudo-code):
public class Book {
public Book(...) {
...
}
public InputComponent<Book> createAdminView() {
return new FormGroup<Book>(
new TextInput(title),
new TextInput(author),
...);
}
}
This way there is no need to publish any of the internal data fields of the object, nobody needs to know how to book looks like, and all changes related to the object will be localized.
In fact, I've been doing this for a couple for years now, and this design results in much easier to maintain code. Have a look at my presentation about Object-Oriented Domain-Driven Design to find out more: https://speakerdeck.com/robertbraeutigam/object-oriented-domain-driven-design
Please look through my code before closing it this time.
The code below works but seems very hacked, I am looking for suggestions on to achieve the same thing with cleaner code or is this as good as it gets.
The code calling the Add and Remove will be from different threads that could possible access the code at the same time, so it must remain thread-safe.
using System;
using System.Collections.Concurrent;
namespace Server
{
public class Company
{
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public ConcurrentDictionary<string, Employee> Employees = new ConcurrentDictionary<string, Employee>();
}
public class Employee
{
public string First { get; set; }
public string Last { get; set; }
public string Ext { get; set; }
}
public class Clients
{
public ConcurrentDictionary<string, Company> CompaniesDict = new ConcurrentDictionary<string, Company>();
public bool Add_Company(string ID, string Name, string Address, string Phone) //This function works
{
Company MyCompany = new Company();
Employee MyEmployees = new Employee();
MyCompany.Name = Name;
MyCompany.Address = Address;
MyCompany.Phone = Phone;
MyCompany.Employees = MyEmployees;
return CompaniesDict.TryAdd(ID, MyCompany);
}
public bool Remove_Company(string ID) //This function works
{
return CompaniesDict.TryRemove(ID, Company tCompany);
}
//This is were I need the help this seems so hacked. Im not trying to update the key, but the value intstead
public bool Set_CompanyName(string ID, string Name)
{
CompaniesDict.TryGetValue(ID, out Company oCompany);
Company nCompany;
nCompany = oCompany;
nCompany.Name = Name;
return CompaniesDict.TryUpdate(ID, nCompany, oCompany);
}
public string Get_CompanyName(string ID)
{
CompaniesDict.TryGetValue(ID, out Company tCompany);
return tCompany.Name;
}
}
}
Please don't just close this and link me to some useless code you call a duplicate. Sorry to be so blunt but this has recently happened to me by a fellow coder on this site. If you have questions that I can answer so that you can full help me please ask them.
Thanks for you help in advance.
There is a much easier approach, as you are updating a field on an object.
Please note that I don't have C# installed on my current PC, so can't validate this.
Note that I declare the out parameter, but don't construct a new one that would be destroyed immediately and I modify the object itself.
i.e.
Company company;
not
Company company=new Company();
This will still not be deterministic if multiple threads call SetCompanyName(), as the new name is updated on the live object and there could be a potential race condition. However the Add and Remove will be, even if Remove removes the company instance just before its name is updated.
public bool Set_CompanyName(string ID, string Name)
{
Company company;
var retval= CompaniesDict.TryGetValue(ID, out company)
if (retval) {
company.Name=Name; // Update your company object directly
}
//else Do something such as throw an exception if it's not found
return retval;
}
I've been going through the Spring Data Cassandra documentation (http://docs.spring.io/spring-data/cassandra/docs/1.0.1.RELEASE/reference/html/cassandra.core.html)
Basically, with proper annotation, I hoped the CassandraTemplate maps a row to a POJO object, but it didn't work as I expected.
For the call,
cassandraOps.queryForObject(s, Person.class)
I received an error as following:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to Person
Anything that I'm missing? Following is the same copy and paste from the doc above.
Person Class looks like:
#Table
public class Person {
#PrimaryKey
private String id;
private String name;
private int age;
public Person(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
#Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
and the application class looks like...:
public class CassandraApp {
private static final Logger LOG = LoggerFactory.getLogger(CassandraApp.class);
private static Cluster cluster;
private static Session session;
public static void main(String[] args) {
try {
cluster = Cluster.builder().addContactPoints(InetAddress.getLocalHost()).build();
session = cluster.connect("mykeyspace");
CassandraOperations cassandraOps = new CassandraTemplate(session);
cassandraOps.insert(new Person("1234567890", "David", 40));
Select s = QueryBuilder.select().from("person");
s.where(QueryBuilder.eq("id", "1234567890"));
LOG.info(cassandraOps.queryForObject(s, Person.class).getId());
cassandraOps.truncate("person");
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
CassandraTemplate's queryForObject(String,Class) is not meant for arbitrary object mapping. It is modeled after JdbcTemplate's queryForObject(String,Class) method. It's intended to take types that the Cassandra driver can convert directly.
To convert arbitrary application-defined classes, use queryForObject(String,RowMapper<T>) or one of its overloads. CqlTemplate doesn't know how to map arbitrary classes; you have to supply the RowMapper<T> implementation for your class T.
you can do it like this way:-
String myQuery = "select * from person where id=1234567890";
Person personObj = cassandraOperations.selectOne(myQuery, Person.class);
<<
For all
List<Person> personListObj = cassandraOperations.select(myQuery, Person.class); >>
this work for me using cassandraTemplete object perfectly... didn't try for cassandraOperation.
also you might need #Column(value = "your_columnName_in_DB") if your pojo class's variable name is different
like
#Column(value = "name")
private String userName;
#Column(value = "age")
private int userAge;
revert here if its work?
Also can you help me pass dynamic value to that myQuery string.. using object[] same like prepareStatment in SQL
thanks.
I'm using PrimeFaces to display information in a DataTable. The data in the datatable is actually a List of entity objects which is generated by OpenJPA. And the entities have relations to other entities. This means that there are Lists inside Lists.
For example, an entity called Authors, which has many Books. The data List<Authors> is listed in the datatable. And in order to sort the List i use Collections.sort() which of course doesn't work when i try to sort on the Authors book title. Because the title field is an instance of the Book class.
How do i go about to sort the Lists when there are relationships like this?
Thanks in advance.
Here is an example of sorting by books title, you need to sort your books list :
public static void main(String args[]) {
List<Author> list = new ArrayList<Author>();
for (int i=0;i<5;i++){
Author a = new Author();
a.setName("author"+i);
List<Book> books = new ArrayList<Book>();
for(int j=0;j<4;j++){
Random r = new Random();
char c = (char) (r.nextInt(26) + 'a');
Book b = new Book();
b.setTitle(c+"title");
books.add(b);
}
a.setBooks(books);
list.add(a);
}
/*
* At this point of time you have Authors list which you want to sort by book title.
* So you can do something like below if you want to do it through Collections.sort
*/
for(Author a : list){
Collections.sort(a.getBooks(), new Comparator<Book>(){
#Override
public int compare(Book o1, Book o2) {
return o1.getTitle().compareToIgnoreCase(o2.getTitle());
}});
}
System.out.println(list);
}
Author and Book used in above example:
import java.util.List;
public class Author {
private List<Book> books;
private String name;
#Override
public String toString() {
return "Author [books=" + books + ", name=" + name + "]";
}
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Book.java:
public class Book {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
#Override
public String toString() {
return "Book [title=" + title + "]";
}
}